diff options
author | Karl Berry <karl@freefriends.org> | 2019-05-02 21:10:43 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2019-05-02 21:10:43 +0000 |
commit | 8c5676efab90bcd994e66dd4c3ef5e64da0cb38d (patch) | |
tree | 5289f9b7c7ad9eda0590e9021a0201c7fffa1cc5 /Master/texmf-dist/tex/luatex | |
parent | 030b6070c99b9d787a0395499d75854156d46099 (diff) |
luatexko (2may19)
git-svn-id: svn://tug.org/texlive/trunk@50924 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
-rw-r--r-- | Master/texmf-dist/tex/luatex/luatexko/luatexko-core.sty | 506 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua | 13 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua | 40 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/luatexko/luatexko.lua | 3182 | ||||
-rw-r--r-- | Master/texmf-dist/tex/luatex/luatexko/luatexko.sty | 1101 |
5 files changed, 2061 insertions, 2781 deletions
diff --git a/Master/texmf-dist/tex/luatex/luatexko/luatexko-core.sty b/Master/texmf-dist/tex/luatex/luatexko/luatexko-core.sty deleted file mode 100644 index 5572bc4ff06..00000000000 --- a/Master/texmf-dist/tex/luatex/luatexko/luatexko-core.sty +++ /dev/null @@ -1,506 +0,0 @@ -% luatexko-core.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. - -\begingroup\expandafter\expandafter\expandafter\endgroup -\expandafter\ifx\csname selectfont\endcsname\relax - \input luaotfload.sty - \ifcsname ifpdf\endcsname\else \input ifpdf.sty \fi - \catcode`@ = 11 -\else - \ProvidesPackage{luatexko-core}[2019/03/24 v1.23 Typesetting Korean with LuaTeX] - \RequirePackage{luaotfload}[2016/04/21] - \RequirePackage{ifpdf} -\fi - -\ifdefined\fontid\else - \let\fontid\luatexfontid % latex 2015/10/01 -\fi -\ifdefined\newattribute\else % luaotfload 2.6 - \let\newattribute\newluatexattribute - \let\unsetattribute\unsetluatexattribute - \let\scantextokens\luatexscantextokens - \let\luaescapestring\luatexluaescapestring -\fi -\ifdefined\pdfliteral\else % luatex 0.87 - \protected\def\pdfliteral{\pdfextension literal} -\fi - -%% make hangul catcode 12 -\count@"1100 -\loop \catcode\count@ 12 - \ifnum\count@<"115F \advance\count@\@ne \repeat -\count@"3131 -\loop \catcode\count@ 12 - \ifnum\count@<"318E \advance\count@\@ne \repeat -\count@"A960 -\loop \catcode\count@ 12 - \ifnum\count@<"A97C \advance\count@\@ne \repeat -\count@"AC00 -\loop \catcode\count@ 12 - \ifnum\count@<"D7A3 \advance\count@\@ne \repeat - -%% attributes -\newattribute\finemathattr \finemathattr\tw@ % finemath on by default -\newattribute\cjtypesetattr -\newattribute\dotemphattr -\newattribute\autojosaattr -\newattribute\luakorubyattr \chardef\luakorubyattrnum\the\allocationnumber -\newattribute\hangulfntattr -\newattribute\hanjafntattr -\newattribute\fallbackfntattr -\newattribute\hangulpunctsattr - -%% load lua modules -\directlua{require "luatexko"} - -%% japanese/chinese/pre-modern -\protected\def\japanese{\frenchspacing\cjtypesetattr\@ne \parindent1em } -\protected\def\Schinese{\frenchspacing\cjtypesetattr\tw@ \parindent2em } -\protected\def\Tchinese{\frenchspacing\cjtypesetattr\thr@@\parindent2em } -\let\chinese\Schinese -% korean in japanese/chinese env. -\protected\def\korean{\unsetattribute\cjtypesetattr} - -% inhibitglue -\protected\def\inhibitglue{{\unsetattribute\cjtypesetattr\hskip\z@skip}} - -%% auto josa -\def\josaignoreparens{\afterassignment\josa@ignore@parens\count@} -\def\josa@ignore@parens{% - \ifcase\count@ \chardef\josa@attr@level\@ne - \else \chardef\josa@attr@level\tw@ - \fi -} -\josaignoreparens=\z@ - -\protected\def\은{{\autojosaattr\josa@attr@level 은}} -\let\는\은 - -\protected\def\을{{\autojosaattr\josa@attr@level 을}} -\let\를\을 - -\protected\def\와{{\autojosaattr\josa@attr@level 와}} -\let\과\와 - -\protected\def\가{{\autojosaattr\josa@attr@level 가}} -\protected\def\이{{\autojosaattr\josa@attr@level 이}} -\protected\def\라{\이라} - -\protected\def\으{{\autojosaattr\josa@attr@level 으}} -\protected\def\로{\으로} - -%% dot-emphasis -\def\dotemphraise{0.4em } -\unless\ifdefined\bfseries\let\bfseries\bf\fi -\def\dotemphchar{\bfseries ^^^^02d9} -\newcount\luakodotemphcnt \luakodotemphcnt\z@ -\protected\def\dotemph#1{{% - \ifnum\luakodotemphcnt > 65535 \luakodotemphcnt\z@ \fi - \global\advance\luakodotemphcnt\@ne - \setbox\z@\hbox{\raise\dotemphraise\hbox{\dotemphchar}}\ht\z@\z@\dp\z@\z@ - \directlua{luatexko.dotemphnode[\the\luakodotemphcnt] = node.copy_list(tex.box[0])}% - \dotemphattr\luakodotemphcnt #1}} - -%% ruby : ruby.sty by Lemberg is excellent; but -%% it doesn't match well with luatexko, so implemented anew -%% with plain tex support and enhanced features. -\def\rubysize{0.6} % size of ruby compared to base character -\def\rubysep{0.1ex} % distance between base character and ruby -% users can define \rubyfont : eg. \font\rubyfont={foo:+ruby} -\newif\ifruby@overlap -\let\rubyoverlap\ruby@overlaptrue -\let\rubynooverlap\ruby@overlapfalse -\rubyoverlap % ruby may protrude over adjacent chars -\newcount\luakorubyattrcnt\luakorubyattrcnt\z@ -\protected\def\ruby#1#2{% - \ifnum\luakorubyattrcnt > 65535 \luakorubyattrcnt\z@ \fi - \global\advance\luakorubyattrcnt\@ne - \begingroup - \setbox\z@\hbox attr\luakorubyattrnum\luakorubyattrcnt{% base character - \vrule width\z@ height.88em depth\z@\relax #1}% - \setbox\tw@\hbox{% ruby - \ifdefined\rubyfont \rubyfont \fi - \ifcsname selectfont\endcsname % latex - \dimen@\dimexpr\rubysize em\relax - \fontsize\dimen@\dimen@\selectfont - \else % plain - \luatexko@resize@font\font\resized@font\font\rubysize - \ifdefined\hangul@font - \luatexko@resize@font\hangulfont{}\hangul@font\rubysize - \fi - \ifdefined\hanja@font - \luatexko@resize@font\hanjafont{}\hanja@font\rubysize - \fi - \ifdefined\fallback@font - \luatexko@resize@font\fallbackfont{}\fallback@font\rubysize - \fi - \fi #2}\ht\tw@\z@\dp\tw@\z@ - \directlua{% - luatexko.rubynode[\the\luakorubyattrcnt] = { - node.copy_list(tex.box[2]), - \number\numexpr\dimexpr\rubysep\relax\relax, - \ifruby@overlap true\else false\fi}}% - \leavevmode\box\z@ - \endgroup} - -\unless\ifcsname selectfont\endcsname -% font resize under plain tex -\begingroup\edef\x{\endgroup - \def\noexpand\rem@vesizepart##1\detokenize{ at}##2\noexpand\end{##1}}\x -\def\luatexko@resize@font#1#2#3#4{% - \begingroup - \edef\x@{\noexpand\rem@vesizepart\fontname#3\detokenize{ at}\noexpand\end}% - \edef\y@{\endgroup#1\noexpand#2 \x@\space at#4\dimexpr\fontdimen6 #3\relax}% - \y@#2} -\fi - -% xxruby: consecutive ruby annotations -\protected\def\xxruby#1#2{% - \begingroup\def\basestr{#1}\def\rubystr{#2}\luako@xxrubyxx\endgroup -} -\def\luako@xxrubyxx{% - \ifx\empty\basestr\else - \expandafter\luako@ruby@get@base@char\basestr\end - \expandafter\luako@ruby@get@ruby@char\rubystr\end - \ruby\basechar\rubychar - \expandafter\luako@xxrubyxx - \fi -} -\def\luako@ruby@get@base@char#1#2\end{\def\basechar{#1}\def\basestr{#2}} -\def\luako@ruby@get@ruby@char#1#2\end{\def\rubychar{#1}\def\rubystr{#2}} - -%% vertical typesetting -%% EXPERIMENTAL code -\def\luatexko@vertical{% - \ifnum\cjtypesetattr<\@ne - \cjtypesetattr\z@ % korean-vertical - \spaceskip 0.5em plus 0.15em minus 0.06em - \parindent 1em - \fi -} -\protected\def\vertical#1{% - \leavevmode - \setbox\z@\vbox\bgroup - \hsize#1\relax - \luatexko@vertical - \everypar{}% - \leftskip\z@ - \rightskip\z@ } -\protected\def\endvertical{\egroup\luatexkorotatebox\z@\box\z@} -\protected\def\luatexkorotatebox#1{% - \ifvbox#1\count@\@ne\else\count@\z@\fi - \dimen@\wd#1\dimen@ii\dimexpr\ht#1+\dp#1\relax - \setbox#1\ifnum\count@=\@ne - \vbox to\dimen@\bgroup\hbox to\dimen@ii\bgroup\hfil\else - \hbox to\dimen@ii\bgroup\vbox to\dimen@\bgroup\fi - \wd#1\z@ \ht#1\z@ \dp#1\z@ - \luatexko@rotate@clockwise{\box#1}% - \ifnum\count@=\@ne\egroup\vfil\else\vfil\egroup\hfil\fi\egroup} -\def\luatexko@rotate@clockwise#1{% - \ifpdf\pdfliteral{q 0 -1 1 0 0 0 cm}#1\pdfliteral{Q}% - \else\special{pdf:btrans rotate -90}#1\special{pdf:etrans}% - \fi -} - -% do not veticalize headline/footline -\ifcsname selectfont\endcsname % latex - \def\verticaltypesetting{% - \maxdepth\z@ \luatexko@vertical - \dimen@\textwidth \textwidth\textheight \textheight\dimen@ - \edef\@outputpage{\unexpanded{\luatexkorotatebox\@outputbox - \textwidth\textheight}\unexpanded\expandafter{\@outputpage}}} -\else % plain - \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@ \luatexko@vertical} -\fi - -% horizontal box inside vertical typesetting -\protected\def\horizontal#1{% - \setbox\z@\vbox\bgroup - \ifnum\cjtypesetattr<\@ne - \unsetattribute\cjtypesetattr - \spaceskip\fontdimen2\font - plus\fontdimen3\font - minus\fontdimen4\font - \fi - \hsize#1\relax - \everypar{}% - \leftskip\z@ - \rightskip\z@ - \parindent\z@ -} -\protected\def\endhorizontal{\egroup\luatexkounrotatebox\z@\box\z@} -\protected\def\luatexkounrotatebox#1{% - \leavevmode - \vbox to\wd#1\bgroup - \vfill - \hbox to\dimexpr\ht#1+\dp#1\bgroup - \wd#1\z@\ht#1\z@\dp#1\z@ - \luatexko@rotate@counterclockwise{\box#1}% - \hfill - \egroup - \egroup -} -\def\luatexko@rotate@counterclockwise#1{% - \ifpdf\pdfliteral{q 0 1 -1 0 0 0 cm}#1\pdfliteral{Q}% - \else \special{pdf:btrans rotate 90}#1\special{pdf:etrans}% - \fi -} - -%% underline using post_linebreak_filter -%% ulem doesn't work well with luatexko; -%% especially linebreak does not occur inside cjk words. -\def\ulinewidth{0.04em } -\def\ulinedown {0.24em } -\newcount\luakoulineboxcnt\luakoulineboxcnt\z@ -\chardef\luako@uline@level\z@ % for nested uline -\def\luako@nested@ulinedown{% must be used in a group - \ifcase\luako@uline@level\else - \edef\ulinedown{\the\dimexpr\ulinedown+0.1em\relax}% - \fi - \chardef\luako@uline@level\@ne} -\def\luako@uline@code@common{% - \begingroup - \ifnum\luakoulineboxcnt > 65535 \luakoulineboxcnt\z@ \fi - \global\advance\luakoulineboxcnt\@ne - \count@\luakoulineboxcnt - \leavevmode} -\protected\def\ulineluatexko#1{% - \luako@uline@code@common - \luako@nested@ulinedown - \dimen@\dimexpr\ulinedown+\ulinewidth\relax - \dimen@ii-\dimexpr\ulinedown\relax - \setbox\z@\hbox{\vrule width\z@ height\dimen@ii depth\dimen@}% - \directlua{ luatexko.uline_boundary(\the\count@,tex.box[0].head) }% - #1\relax - \directlua{ luatexko.uline_boundary(\the\count@) }% - \endgroup} -\let\uline\ulineluatexko -\protected\def\sout#1{{\def\ulinedown{-0.5ex-\ulinewidth/2}\ulineluatexko{#1}}} -\protected\def\uuline#1{\ulineluatexko{\ulineluatexko{#1}}} -% ulem with arbitray box -\protected\def\markoverwith#1#2{% - \luako@uline@code@common - \setbox\z@\hbox{#1}% - \directlua{ luatexko.uline_boundary(\the\count@,tex.box[0]) }% - #2\relax - \directlua{ luatexko.uline_boundary(\the\count@) }% - \endgroup} -\protected\def\uwave{\ifx\ltxko@sixly\undefined \font\ltxko@sixly=lasy6 \fi - \markoverwith{\lower4\p@\hbox{\ltxko@sixly\char58}}} -\protected\def\xout {\markoverwith{\hbox to.35em{/\hss}}} -\protected\def\dashuline#1{\begingroup - \luako@nested@ulinedown - \markoverwith{\kern.125em\vtop{\kern\ulinedown - \hrule width.3em height\z@ depth\ulinewidth}\kern.125em}{#1}\endgroup} -\protected\def\dotuline#1{\begingroup - \luako@nested@ulinedown - \dimen@\dimexpr\ulinedown+0.1ex\relax - \markoverwith{\lower\dimen@\hbox{\kern.1em .\kern.04em}}{#1}\endgroup} - -%% hangul font -- core -\def\luatexko@hangul@selectfont{% - \ifdefined\hangul@font - \begingroup - \let\luatexko@hangul@selectfont\relax - \let\luatexko@hanja@selectfont\relax - \let\luatexko@fallback@selectfont\relax - \hangul@font - \expandafter\expandafter\expandafter - \endgroup - \expandafter\hangulfntattr\fontid\font\relax - \fi -} -\def\luatexko@hanja@selectfont{% - \ifdefined\hanja@font - \begingroup - \let\luatexko@hangul@selectfont\relax - \let\luatexko@hanja@selectfont\relax - \let\luatexko@fallback@selectfont\relax - \hanja@font - \expandafter\expandafter\expandafter - \endgroup - \expandafter\hanjafntattr\fontid\font\relax - \fi -} -\def\luatexko@fallback@selectfont{% - \ifdefined\fallback@font - \begingroup - \let\luatexko@hangul@selectfont\relax - \let\luatexko@hanja@selectfont\relax - \let\luatexko@fallback@selectfont\relax - \fallback@font - \expandafter\expandafter\expandafter - \endgroup - \expandafter\fallbackfntattr\fontid\font\relax - \fi -} - -% user interface for plain tex -\protected\def\hangulfont - {\afterassignment\luatexko@hangul@selectfont\font\hangul@font} -\protected\def\hanjafont - {\afterassignment\luatexko@hanja@selectfont \font\hanja@font } -\protected\def\fallbackfont - {\afterassignment\luatexko@fallback@selectfont\font\fallback@font} - -%% hangul in math mode -- general -\def\setmathhangulblock#1#2{% - \count@="#1 - \loop - \Umathcode\count@ = 7 \symmathhangul \count@ - \ifnum\count@<"#2 - \advance\count@\@ne - \repeat} -\unless\ifcsname selectfont\endcsname - % for plain; see luatexko.sty for latex - \newfam\symmathhangul - \def\setmathhangulfont#1#{\setmathhangul@font@} - \def\setmathhangul@font@#1{% - \font\tenhangul = "#1" at 10pt - \font\sevenhangul = "#1" at 7pt - \font\fivehangul = "#1" at 5pt - \textfont\symmathhangul = \tenhangul - \scriptfont\symmathhangul = \sevenhangul - \scriptscriptfont\symmathhangul = \fivehangul} - \setmathhangulblock{AC00}{D7A3} -\fi - -%% hangul normalization -\def\luatexhangulnormalize{\directlua{require "luatexko-normalize"}% - \afterassignment\luatexh@ngulnorm@lize\count@} -\def\luatexh@ngulnorm@lize{% - \ifcase\count@ % 0: none - \directlua{ luatexkonormalize.unload() }% - \or % 1: nfc - \directlua{ luatexkonormalize.compose() }% - \else % 2: nfd - \directlua{ luatexkonormalize.decompose() }% - \fi} - -%% uhc to utf8 -\def\luatexuhcinputencoding{\directlua{require "luatexko-uhc2utf8"}% - \afterassignment\luatexuhcinput@ncoding\count@} -\def\luatexuhcinput@ncoding{% - \ifcase\count@ - \directlua{ luatexkouhc2utf8.stopconvert() }% - \else - \directlua{ luatexkouhc2utf8.startconvert() }% - \fi} - -%% uhc file names -\def\luatexuhcfilenames{\directlua{require "luatexko-uhc2utf8"}% - \afterassignment\luatexuhcfilen@mes\count@} -\def\luatexuhcfilen@mes{% - \ifcase\count@ - \directlua{ luatexkouhc2utf8.stop_uhc_filename() }% - \else - \directlua{ luatexkouhc2utf8.start_uhc_filename() }% - \fi} - -%% use hangul font for hangul chars -\def\hangulfontforhangul{\afterassignment\h@ngul@fontforh@ngul\count@} -\let\hangulbyhangulfont\hangulfontforhangul -\def\h@ngul@fontforh@ngul{% - \ifcase\count@ - \directlua{ luatexko.hangulfontforhangul = false }% - \else - \directlua{ luatexko.hangulfontforhangul = true }% - \fi} - -%% use hanja font for hanja chars -\def\hanjafontforhanja{\afterassignment\h@nj@fontforh@nj@\count@} -\let\hanjabyhanjafont\hanjafontforhanja -\def\h@nj@fontforh@nj@{% - \ifcase\count@ - \directlua{ luatexko.hanjafontforhanja = false }% - \else - \directlua{ luatexko.hanjafontforhanja = true }% - \fi} - -% latin punctuations typeset with hangul fonts -\def\hangulpunctuations{\afterassignment\h@ngulpunctu@tions\count@} -\def\h@ngulpunctu@tions{% - \ifcase\count@ - \unsetattribute\hangulpunctsattr - \else - \hangulpunctsattr\@ne - \fi -} -\def\registerpunctuations#1{\registerpunctu@tions#1,*,} -\def\registerpunctu@tions#1,{% - \ifx\empty#1\empty - \expandafter\registerpunctu@tions - \else\ifx*#1\else - \directlua{ luatexko.hangulpunctuations[\number#1]=true } - \expandafter\expandafter\expandafter\registerpunctu@tions - \fi\fi -} -\def\unregisterpunctuations#1{\unregisterpunctu@tions#1,*,} -\def\unregisterpunctu@tions#1,{% - \ifx\empty#1\empty - \expandafter\unregisterpunctu@tions - \else\ifx*#1\else - \directlua{ luatexko.hangulpunctuations[\number#1]=nil } - \expandafter\expandafter\expandafter\unregisterpunctu@tions - \fi\fi -} - -% Actual Text: beware that #2 should not be divided into separate pages. -% \actualtext* variant inserts space char from phvr8r. see accsupp manual -\protected\def\actualtext#1#{% - \ifx\empty#1\empty - \expandafter\actualtext@\expandafter!% - \else - \expandafter\actualtext@\expandafter*% - \fi -} -\def\actualtext@{% - \begingroup - \catcode`\\=12 \catcode`\$=12 \catcode`\&=12 \catcode`\#=12 - \catcode`\^=12 \catcode`\_=12 \catcode`\~=12 %catcode`\%=12 - \actualtext@@ -} -\def\actualtext@@#1#2{% - \endgroup - \luatexko@pdfspecial{/Span<</ActualText\directlua{ - luatexko.actualtext("\luaescapestring{\detokenize{#2}}") - }>>BDC}% - \scantextokens{#2}% - \ifx*#1% - \begingroup - \ifdefined\actualtext@spacefont\else - \global\font\actualtext@spacefont=phvr8r\relax - \fi - \setbox\z@\hbox{\actualtext@spacefont\char32}% - \wd\z@\z@\ht\z@\z@\dp\z@\z@ - \leavevmode\copy\z@ - \endgroup - \fi - \luatexko@pdfspecial{EMC}% -} -\def\luatexko@pdfspecial#1{% - \ifpdf\pdfliteral page{#1}% - \else\special{pdf:content #1}% - \fi -} - -%% 말줄임표 -\def\hellipsis{^^^^2026^^^^2026} - -\endinput diff --git a/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua b/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua index b791a2497ff..895ee5b60c3 100644 --- a/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua +++ b/Master/texmf-dist/tex/luatex/luatexko/luatexko-normalize.lua @@ -13,8 +13,8 @@ luatexbase.provides_module({ name = "luatexko-normalize", - version = "1.23", - date = "2019/03/24", + version = "2.0", + date = "2019/05/01", author = "Dohyun Kim, Soojin Nam", description = "Hangul normalization", license = "LPPL v1.3+", @@ -408,7 +408,6 @@ local byte = unicodeutf8.byte local char = unicodeutf8.char local find = unicodeutf8.find local concat = table.concat -local floor = math.floor local add_to_callback = luatexbase.add_to_callback local remove_from_callback = luatexbase.remove_from_callback @@ -426,15 +425,15 @@ end local syllable2jamo = function(s) s = byte(s) - 0xac00 local t = {} - t[1] = char(floor(s / 588) + 0x1100) - t[2] = char(floor(s % 588 / 28) + 0x1161) + t[1] = char(s // 588 + 0x1100) + t[2] = char(s % 588 // 28 + 0x1161) local jong = s % 28 t[3] = jong > 0 and char(jong + 0x11a7) or nil return concat(t) end local hanguldecompose = function(buffer) - return gsub(buffer, "[가-힣]", syllable2jamo) + return gsub(buffer, "[\234\176\128-\237\158\163]", syllable2jamo) end local function hanjanormalize(c) @@ -457,7 +456,7 @@ local function jamo2cjamojung(c,t) end local hangulcompose = function(buffer) - buffer = gsub(buffer, "[가-힣]"..jong, hanguldecompose) + buffer = gsub(buffer, "[\234\176\128-\237\158\163]"..jong, hanguldecompose) buffer = gsub(buffer, "("..ncho..")("..njung..")("..jong.."?)", jamo2syllable) buffer = gsub(buffer, "([\225\132\128-\225\133\153])\225\133\160", jamo2cjamocho) diff --git a/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua b/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua index 70150f346ea..d23de4bc968 100644 --- a/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua +++ b/Master/texmf-dist/tex/luatex/luatexko/luatexko-uhc2utf8.lua @@ -13,8 +13,8 @@ luatexbase.provides_module({ name = "luatexko-uhc2utf8", - version = "1.23", - date = "2019/03/24", + version = "2.0", + date = "2019/05/01", author = "Dohyun Kim, Soojin Nam", description = "UHC (CP949) input encoding", license = "LPPL v1.3+", @@ -24,12 +24,12 @@ luatexkouhc2utf8 = luatexkouhc2utf8 or {} local luatexkouhc2utf8 = luatexkouhc2utf8 local format = string.format +local utf8len = utf8.len require "unicode" local unicodeutf8 = unicode.utf8 local ugsub = unicodeutf8.gsub local ubyte = unicodeutf8.byte local uchar = unicodeutf8.char -local floor = math.floor local kpse_find_file = kpse.find_file local add_to_callback = luatexbase.add_to_callback local remove_from_callback = luatexbase.remove_from_callback @@ -58,38 +58,10 @@ end local t_uhc2ucs = t_uhc2ucs or get_uhc_uni_table() -local function not_utf8lowbyte(t) - for _,v in ipairs(t) do - -- rough checking - if v < 0x80 or v > 0xBF then return true end - end - return false -end - local uhc_to_utf8 = function(buffer) if not buffer then return end - -- check if buffer is already utf-8; better solution? - local i, buflen = 1, buffer:len()+1 - while i < buflen do - local a = buffer:byte(i) - if a < 0x80 then - i = i + 1 - elseif a < 0xC2 then - break - elseif a < 0xE0 then - if not_utf8lowbyte({buffer:byte(i+1)}) then break end - i = i + 2 - elseif a < 0xF0 then - if not_utf8lowbyte({buffer:byte(i+1,i+2)}) then break end - i = i + 3 - elseif a < 0xF5 then - if not_utf8lowbyte({buffer:byte(i+1,i+3)}) then break end - i = i + 4 - else - break - end - end - if i == buflen then return nil end + -- check if buffer is already utf-8 + if utf8len(buffer) then return nil end -- now convert to utf8 buffer = buffer:gsub("([\129-\253])([\65-\254])", function(a, b) @@ -128,7 +100,7 @@ local function utf8_to_uhc (name) function(u) local c = t_ucs2uhc[ubyte(u)] if c then - return format("%c%c", floor(c/256), c%256) + return format("%c%c", c//256, c%256) end end) return name diff --git a/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua b/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua index 73acf5a4291..01a274bfeda 100644 --- a/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua +++ b/Master/texmf-dist/tex/luatex/luatexko/luatexko.lua @@ -1,7 +1,7 @@ -- luatexko.lua -- --- Copyright (c) 2013-2019 Dohyun Kim <nomos at ktug org> --- Soojin Nam <jsunam at gmail com> +-- 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 @@ -13,1902 +13,1307 @@ luatexbase.provides_module { name = 'luatexko', - date = '2019/04/06', - version = '1.24', - description = 'Korean linebreaking and font-switching', + date = '2019/05/01', + version = '2.0', + description = 'typesetting Korean with LuaTeX', author = 'Dohyun Kim, Soojin Nam', license = 'LPPL v1.3+', } -luatexko = luatexko or {} -local luatexko = luatexko - -local dotemphnode,rubynode = {},{} -luatexko.dotemphnode = dotemphnode -luatexko.rubynode = rubynode - -local stringbyte = string.byte -local stringchar = string.char -local stringformat = string.format -local mathfloor = math.floor -local tex_round = tex.round -local tex_sp = tex.sp -local texcount = tex.count -local fontdefine = font.define - -local kpse_find_file = kpse.find_file - -local fontdata = fonts.hashes.identifiers - -local finemathattr = luatexbase.attributes.finemathattr -local cjtypesetattr = luatexbase.attributes.cjtypesetattr -local dotemphattr = luatexbase.attributes.dotemphattr -local autojosaattr = luatexbase.attributes.autojosaattr -local luakorubyattr = luatexbase.attributes.luakorubyattr -local hangulfntattr = luatexbase.attributes.hangulfntattr -local hanjafntattr = luatexbase.attributes.hanjafntattr -local fallbackfntattr = luatexbase.attributes.fallbackfntattr -local hangulpunctsattr = luatexbase.attributes.hangulpunctsattr -local luakoglueattr = luatexbase.new_attribute("luakoglueattr") -local luakounicodeattr = luatexbase.new_attribute("luakounicodeattr") -local luakoulineattr = luatexbase.new_attribute("luakoulineattr") - -local function warn (...) - return luatexbase.module_warning("luatexko", stringformat(...)) +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 texcount = tex.count +local texround = tex.round +local texset = tex.set +local texsp = tex.sp + +local stringformat = string.format + +local tableconcat = table.concat +local tableinsert = table.insert + +local add_to_callback = luatexbase.add_to_callback +local attributes = luatexbase.attributes +local call_callback = luatexbase.call_callback +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 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 xleaders = 102 +local nohyphen = registernumber"l@nohyphenation" or -1 -- verbatim + +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 add_to_callback (name, func, desc, pos) - local t = {{func, desc}} - if pos then - for i,v in ipairs(luatexbase.callback_descriptions(name)) do - if i >= pos then - t[#t+1] = {luatexbase.remove_from_callback(name, v)} - end - end - end - for i,v in ipairs(t) do - luatexbase.add_to_callback(name, v[1], v[2]) - 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 glue_type_space = 13 -local kern_type_itlc = 3 - -local discnode = node.id("disc") -local gluenode = node.id("glue") -local glyphnode = node.id("glyph") -local mathnode = node.id("math") -local hlistnode = node.id("hlist") -local vlistnode = node.id("vlist") -local kernnode = node.id("kern") -local penaltynode = node.id("penalty") -local rulenode = node.id("rule") -local whatsitnode = node.id("whatsit") -local user_whatsit = node.subtype("user_defined") -local n_copy_list = node.copy_list - -local nodedirect = node.direct -local d_getdisc = nodedirect.getdisc -local d_todirect = nodedirect.todirect -local d_tonode = nodedirect.tonode -local d_has_field = nodedirect.has_field -local d_copy_node = nodedirect.copy -local d_getid = nodedirect.getid -local d_getfield = nodedirect.getfield -local d_setfield = nodedirect.setfield -local d_getsubtype = nodedirect.getsubtype -local d_setsubtype = nodedirect.setsubtype -local d_getchar = nodedirect.getchar -local d_setchar = nodedirect.setchar -local d_getfont = nodedirect.getfont -local d_getlist = nodedirect.getlist -local d_setlist = nodedirect.setlist -local d_getnext = nodedirect.getnext -local d_setnext = nodedirect.setnext -local d_getprev = nodedirect.getprev -local d_setprev = nodedirect.setprev -local d_setleader = nodedirect.setleader -local d_getglue = nodedirect.getglue -local d_setglue = nodedirect.setglue -local d_has_attribute = nodedirect.has_attribute -local d_set_attribute = nodedirect.set_attribute -local d_unset_attribute = nodedirect.unset_attribute -local d_traverse = nodedirect.traverse -local d_traverse_id = nodedirect.traverse_id -local d_insert_before = nodedirect.insert_before -local d_insert_after = nodedirect.insert_after -local d_remove_node = nodedirect.remove -local d_nodenew = nodedirect.new -local d_nodecount = nodedirect.count -local d_end_of_math = nodedirect.end_of_math -local d_nodetail = nodedirect.slide -- tail seems to be no good -local d_nodedimensions = nodedirect.dimensions -local d_nodefree = nodedirect.free -local d_nodewrite = nodedirect.write -local d_node_zero_glue = nodedirect.is_zero_glue - -local emsize = 655360 - -local cjkclass = { - [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, -- 「 half - [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, -- 」 half - [0xFF64] = 2, -- 、 half - [0x00B7] = 3, -- · - [0x30FB] = 3, -- ・ - [0xFF1A] = 3, -- : - [0xFF1B] = 3, -- ; - [0xFF65] = 3, -- ・ half - [0x3002] = 4, -- 。 - [0xFE12] = 4, -- ︒ - [0xFF0E] = 4, -- . - [0xFF61] = 4, -- 。 half - [0x2015] = 5, -- ― - [0x2025] = 5, -- ‥ - [0x2026] = 5, -- … - [0xFE19] = 5, -- ︙ - [0xFE30] = 5, -- ︰ - [0xFE31] = 5, -- ︱ - [0xFE15] = 6, -- ︕ - [0xFE16] = 6, -- ︖ - [0xFF01] = 6, -- ! - [0xFF1F] = 6, -- ? +local hangul_tonemark = { + [0x302E] = true, [0x302F] = true, } -local inhibitxspcode = setmetatable({ - [0x002D] = 0, -- - hyphen minus - [0x003C] = 0, -- < - [0x003E] = 1, -- > - [0x00B0] = 1, -- ° - [0x2032] = 1, -- ′ - [0x2033] = 1, -- ″ - [0x25CC] = 2, -- dotted circle - [0x3012] = 2, -- 〒 - [0x301C] = 0, -- 〜 - [0xFE14] = 1, -- ︔ - [0xFF1B] = 1, -- ; - [0xFFE5] = 0, -- ¥ -},{ - __index = function (_,c) - if cjkclass[c] == 1 then - return 2 - elseif cjkclass[c] == 2 or cjkclass[c] == 4 or cjkclass[c] == 6 then - return 1 - elseif cjkclass[c] == 5 then - return 0 - end - end -}) +local function is_unicode_vs (c) + return (c >= 0xFE00 and c <= 0xFE0F) + or (c >= 0xE0100 and c <= 0xE01EF) +end -local postbreakpenalty = setmetatable({ - [0x0023] = 500, - [0x0024] = 500, - [0x0025] = 500, - [0x0026] = 500, - [0x0028] = 10000, - [0x003C] = 10000, -- < - [0x005B] = 10000, - [0x0060] = 10000, - [0x007B] = 10000, -- { - [0x00AB] = 10000, -- « - [0x2013] = 50, -- en-dash - [0x2014] = 50, -- em-dash - [0xFF03] = 200, - [0xFF04] = 200, - [0xFF05] = 200, - [0xFF06] = 200, - [0xFF40] = 10000, -- ` -},{ - __index = function (_,c) - if cjkclass[c] == 1 then - return 10000 - 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_vs(c) +end + +local function is_noncjkletter (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_chosong(c) + or is_jungsong(c) + or is_jongsong(c) + or (c >= 0x3131 and c <= 0x318E) +end + +local stretch_f, shrink_f = 5/100, 3/100 -- should be consistent for ruby + +local function get_font_data (fontid) + return fontgetfont(fontid) or fontfonts[fontid] or {} +end + +local function get_en_size (f) + local pm + if type(f) == "number" then + pm = getparameters(f) + elseif type(f) == "table" then + pm = f.parameters end -}) + return pm and pm.quad and pm.quad/2 or 327680 +end -local prebreakpenalty = setmetatable({ - [0x0021] = 10000, - [0x0022] = 10000, - [0x0027] = 10000, - [0x0029] = 10000, - [0x002A] = 5000, -- 500, -- * - [0x002B] = 5000, -- 500, -- + - [0x002C] = 10000, - [0x002D] = 10000, - [0x002E] = 10000, - [0x002F] = 5000, -- 500, -- / - [0x003A] = 10000, - [0x003B] = 10000, - [0x003E] = 10000, -- > - [0x003F] = 10000, - [0x005D] = 10000, - [0x007D] = 10000, -- } - [0x00B4] = 10000, -- ´ - [0x00BB] = 10000, -- » - [0x2013] = 10000, -- – - [0x2014] = 10000, -- — - [0x2025] = 5000, -- 250, -- ‥ - [0x2026] = 5000, -- 250, -- … - [0x2212] = 5000, -- 200, -- − minus sign - [0x3005] = 10000, -- 々 - [0x301C] = 10000, -- 〜 - [0x3035] = 10000, -- 〵 - [0x303B] = 10000, -- 〻 - [0x3041] = 150, - [0x3043] = 150, - [0x3045] = 150, - [0x3047] = 150, - [0x3049] = 150, - [0x3063] = 150, - [0x3083] = 150, - [0x3085] = 150, - [0x3087] = 150, - [0x308E] = 150, - [0x3095] = 150, - [0x3096] = 150, - [0x309B] = 10000, -- ゛ - [0x309C] = 10000, -- ゜ - [0x309D] = 10000, -- ゝ - [0x309E] = 10000, -- ゞ - [0x30A0] = 10000, -- ゠ - [0x30A1] = 150, - [0x30A3] = 150, - [0x30A5] = 150, - [0x30A7] = 150, - [0x30A9] = 150, - [0x30C3] = 150, - [0x30E3] = 150, - [0x30E5] = 150, - [0x30E7] = 150, - [0x30EE] = 150, - [0x30F5] = 150, - [0x30F6] = 150, - [0x30FC] = 10000, -- ー - [0x30FD] = 10000, -- ヽ - [0x30FE] = 10000, -- ヾ - [0xFE13] = 10000, -- ︓ - [0xFE14] = 10000, -- ︔ - [0xFE19] = 5000, -- 250, -- ︙ - [0xFE30] = 5000, -- 250, -- ︰ - [0xFE32] = 10000, -- ︲ - [0xFF0B] = 5000, -- 200, -- + - [0xFF1D] = 5000, -- 200, -- = - [0xFF9E] = 10000, -- ゙ - [0xFF9F] = 10000, -- ゚ -},{ - __index = function (_,c) - if cjkclass[c] and cjkclass[c] > 1 then - return 10000 - 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 xspcode = { - [0x0027] = 2, - [0x0028] = 1, - [0x0029] = 2, - [0x002C] = 2, - [0x002E] = 2, - [0x003B] = 2, - [0x005B] = 1, - [0x005D] = 2, - [0x0060] = 1, -} +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 cjk_glue_spec = { [0] = --- 한자 ( ) · . — ? 한글 초성 중종성 latin -{[0] = nil, {.5,.5}, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --한자 -{[0] = nil, nil, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, -- ( -{[0] = {.5,.5}, {.5,.5}, nil, {.25,.25},nil, {.5,.5}, {.5,.5}, {.25,.25},{.25,.25},{.25,.25},{.5,.5}, }, -- ) -{[0] = {.25,.25},{.25,.25},{.25,.25},{.5,.25}, {.25,.25},{.25,.25},{.25,.25},{.25,.25},{.25,.25},{.25,.25},{.25,.25},}, -- · -{[0] = {.5,0}, {.5,0}, nil, {.75,.25},nil, {.5,0}, {.5,0}, {.5,0}, {.5,0}, {.5,0}, {.5,0}, }, -- . -{[0] = nil, {.5,.5}, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, -- — -{[0] = {.5,.5}, {.5,.5}, nil, {.25,.25},nil, nil, nil, {.5,.5}, {.5,.5}, {.5,.5}, {.5,.5}, }, -- ? --- -{[0] = nil, {.25,.25},nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --한글 -{[0] = nil, {.25,.25},nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --초성 -{[0] = nil, {.25,.25},nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --중종성 -{[0] = nil, {.5,.5}, nil, {.25,.25},nil, nil, nil, nil, nil, nil, nil, }, --latin -} +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 -local latin_fullstop = { - [0x2e] = 1, -- . - -- [0x21] = 2, -- ! - -- [0x2c] = 2, -- , - -- [0x3f] = 2, -- ? - -- [0x2026] = 1, -- \ldots -} +-- font fallback -local hangulpunctuations = luatexko.hangulpunctuations or { +local force_hangul = { [0x21] = true, -- ! [0x27] = true, -- ' [0x28] = true, -- ( [0x29] = true, -- ) [0x2C] = true, -- , - -- [0x2D] = true, -- - [0x2E] = true, -- . [0x3A] = true, -- : [0x3B] = true, -- ; - [0x3C] = true, -- < - [0x3E] = true, -- > [0x3F] = true, -- ? - [0x5B] = true, -- [ - [0x5D] = true, -- ] [0x60] = true, -- ` - [0x7B] = true, -- { - [0x7D] = true, -- } - [0xB7] = true, -- periodcentered - -- [0x2014] = true, -- emdash - -- [0x2015] = true, -- horizontal bar - [0x2018] = true, -- quoteleft - [0x2019] = true, -- quoteright - [0x201C] = true, -- quotedblleft - [0x201D] = true, -- quotedblright - [0x2026] = true, -- ellipsis + [0xB7] = true, -- · + [0x2014] = true, -- — + [0x2015] = true, -- ― + [0x2018] = true, -- ‘ + [0x2019] = true, -- ’ + [0x201C] = true, -- “ + [0x201D] = true, -- ” + [0x2026] = true, -- … [0x203B] = true, -- ※ } -luatexko.hangulpunctuations = hangulpunctuations - -local josa_list = { - -- 리을, 중성, 종성 - [0xAC00] = {0xC774, 0xAC00, 0xC774}, -- 가 = 이, 가, 이 - [0xC740] = {0xC740, 0xB294, 0xC740}, -- 은 = 은, 는, 은 - [0xC744] = {0xC744, 0xB97C, 0xC744}, -- 을 = 을, 를, 을 - [0xC640] = {0xACFC, 0xC640, 0xACFC}, -- 와 = 과, 와, 과 - [0xC73C] = {-1, -1, 0xC73C}, -- 으(로) = , , 으 - [0xC774] = {0xC774, -1, 0xC774}, -- 이(라) = 이, , 이 -} - -local josa_code = { - [0x30] = 3, -- 0 - [0x31] = 1, -- 1 - [0x33] = 3, -- 3 - [0x36] = 3, -- 6 - [0x37] = 1, -- 7 - [0x38] = 1, -- 8 - [0x4C] = 1, -- L - [0x4D] = 3, -- M - [0x4E] = 3, -- N - [0x6C] = 1, -- l - [0x6D] = 3, -- m - [0x6E] = 3, -- n - [0xFB02] = 1, -- fl - [0xFB04] = 1, -- ffl - ng = 3, - ap = 3, - up = 3, - at = 3, - et = 3, - it = 3, - ot = 3, - ut = 3, - ok = 3, - ic = 3, - le = 1, - ime = 3, - ine = 3, - ack = 3, - ick = 3, - oat = 2, - TEX = 3, - -- else 2 -} - -local function d_get_gluenode (...) - local g = d_nodenew(gluenode) - d_setglue(g, ...) - return g -end - -local function d_get_penaltynode (n) - local p = d_nodenew(penaltynode) - d_setfield(p,"penalty", n or 0) - return p -end +luatexko.forcehangulchars = force_hangul -local function d_get_kernnode (n) - local k = d_nodenew(kernnode,1) - d_setfield(k,"kern", n or 0) - return k -end +local forcehf_f, forcehf_id = new_user_whatsit("forcehf","luatexko") -local function d_get_rulenode (w,h,d) - local r = d_nodenew(rulenode) - d_setfield(r,"width", w or 0) - d_setfield(r,"height", h or 0) - d_setfield(r,"depth", d or 0) - return r +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 function d_make_luako_glue(...) - local glue = d_get_gluenode(...) - d_set_attribute(glue,luakoglueattr,1) - return glue -end - -local function is_cjk_k (c) - return (c >= 0x2E80 and c <= 0x9FFF ) - or (c >= 0xF900 and c <= 0xFAFF ) - or (c >= 0xFE10 and c <= 0xFE1F ) - or (c >= 0xFE30 and c <= 0xFE4F ) - or (c >= 0xFF00 and c <= 0xFFEF ) - or (c >= 0x20000 and c <= 0x2FFFD) - or c == 0x00B0 or c == 0x2015 - -- or c == 0x2018 or c == 0x2019 - -- or c == 0x201C or c == 0x201D - or c == 0x2026 or c == 0x2032 - or c == 0x2033 -end +local active_processes = {} -local function is_hanja (c) - return (c >= 0x3400 and c <= 0x9FFF ) - or (c >= 0xF900 and c <= 0xFAFF ) - or (c >= 0x20000 and c <= 0x2FFFD) -end +local char_font_options ={ + interhangul = {}, + interlatincjk = {}, + intercharacter = {}, + hangulspaceskip = {}, + tonemarkwidth = {}, +} -local function is_hangul (c) - return (c >= 0xAC00 and c <= 0xD7A3) +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 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 = { texround(newwd), texround(newwd/2), texround(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 is_chosong (c) - return (c >= 0x1100 and c <= 0x115F) - or (c >= 0xA960 and c <= 0xA97C) -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_unicode_vs(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 -local function is_jungjongsong (c) - return (c >= 0x1160 and c <= 0x11FF) - or (c >= 0xD7B0 and c <= 0xD7Fb) - or c == 0x302E or c == 0x302F -- tone marks -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 no_legacy = format == "opentype" or format == "truetype" + if hf and no_legacy 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 -local function is_unicode_vs (c) - return (c >= 0xFE00 and c <= 0xFE0F ) - or (c >= 0xE0100 and c <= 0xE01EF) -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 -local function get_cjk_class (ch, cjtype) - if ch then - if is_hangul(ch) then return 7 end -- hangul = 7 - if is_chosong(ch) then return 8 end -- jamo LC = 8 - if is_jungjongsong(ch) then return 9 end -- jamo VL, TC, TM = 9 - local c = is_cjk_k(ch) and 0 or 10 -- hanja = 0; latin = 10 - if cjkclass[ch] then c = cjkclass[ch] end -- cjkclass 1 .. 6 - if cjtype then - if cjtype == 2 and - (ch == 0xFF1F or ch == 0xFF01 or ch == 0xFF1A or ch == 0xFF1B) then - c = 4 -- simplified chinese ? ! + props.unicode = c end - else - if ch == 0x2018 or ch == 0x2019 or ch == 0x201C or ch == 0x201D then - c = 10 -- korean “ ” ‘ ’ + 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 - return c + curr = getnext(curr) end + return head end -local type1fonts = {} - -local function get_font_table (fid) - if fid then - if fontdata[fid] then - return fontdata[fid] - elseif type1fonts[fid] then - return type1fonts[fid] - else - return font.fonts[fid] - end - end -end +-- linebreak -local function get_font_char (fid, chr) - local f = get_font_table(fid) - return chr and f and f.characters and f.characters[chr] -end +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 function get_font_emsize(fid) - local f = get_font_table(fid) - return (f and f.parameters and f.parameters.quad) or emsize +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 = function(_,c) return charclass[c] end }) + +local vert_charclass = setmetatable({ + [0xFF1A] = 5, -- 0xFE13 + [0xFF1B] = 5, -- 0xFE14 +}, { __index = function(_,c) return charclass[c] end }) + +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_noncjkletter(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_jamo(c) and not is_jungsong(c) and not is_jongsong(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 get_font_feature (fid, name) - local f = get_font_table(fid) - return f and f.shared and f.shared.features and f.shared.features[name] +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 get_char_boundingbox(fid, chr) - local f = get_font_table(fid) - local glbox = f and f.shared - glbox = glbox and glbox.rawdata - glbox = glbox and glbox.descriptions - glbox = glbox and glbox[chr] and glbox[chr].boundingbox - if glbox then - local factor, bbox = f.parameters and f.parameters.factor or 655.36, {} - for i,v in ipairs(glbox) do - bbox[i] = v * factor +local function ruby_char_font (rb) + local n, c, f = has_glyph(rb.list), 0, 0 + if n then + c, f = my_node_props(n).unicode or n.char, n.font or 0 + if is_chosong(c) or hangul_tonemark[c] then + c = 0xAC00 end - return bbox end + return c, f end -local function d_get_unicode_char(curr) - local uni = d_getchar(curr) or 0 - if (uni > 0xFF and uni < 0xE000) or (uni > 0xF8FF and uni < 0xF0000) then - return uni -- no pua. no nanumgtm - end - if uni < 0xE000 or (uni > 0xF8FF and uni < 0xF0000) then -- no pua - local uchr = get_font_char(d_getfont(curr), uni) - uchr = uchr and uchr.tounicode - uchr = uchr and uchr:sub(1,4) -- seems ok for old hangul - if uchr then return tonumber(uchr,16) 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 - local uatt = d_has_attribute(curr, luakounicodeattr) - if uatt then return uatt end - return uni end -local function d_get_hlist_char_first (hlist) - local curr = d_getlist(hlist) - while curr do - local currid = d_getid(curr) - if currid == glyphnode then - local c,f = d_get_unicode_char(curr), d_getfont(curr) - if c then return c,f end - elseif currid == hlistnode or currid == vlistnode then - local c,f = d_get_hlist_char_first(curr) - if c then return c,f end - elseif currid == gluenode then - if not d_node_zero_glue(curr) then return 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 - curr = d_getnext(curr) + n = getnext(n) end + return curr end -local function d_get_hlist_char_last (hlist,c,f) - local curr = d_nodetail(d_getlist(hlist)) - while curr do - local currid = d_getid(curr) - if currid == glyphnode then - c, f = d_get_unicode_char(curr), d_getfont(curr) - if c and not is_unicode_vs(c) then break end -- bypass VS - elseif currid == hlistnode or currid == vlistnode then - c, f = d_get_hlist_char_last(curr) - if c then break end - elseif currid == gluenode then - if not d_node_zero_glue(curr) then break 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 - curr = d_getprev(curr) + return dim end - return c, f end ----------------------------- --- cjk linebreak and spacing ----------------------------- -local function kanjiskip (head,curr) - d_insert_before(head,curr,d_make_luako_glue(0, emsize*0.1, emsize*0.02)) -end - -local function xkanjiskip (head,curr) - if d_has_attribute(curr,finemathattr) == 0 then -- ttfamily - kanjiskip(head,curr) - else - d_insert_before(head,curr,d_make_luako_glue(0.25*emsize, emsize*0.15, emsize*0.06)) +local function get_font_opt_dimen (fontid, option_name) + local fop_t = char_font_options[option_name] + local dim = fop_t and fop_t[fontid] + if dim == nil then + local fd = get_font_data(fontid) + dim = fontdata_opt_dim(fd, option_name) + if dim then + fop_t[fontid] = dim -- cache + else + fop_t[fontid] = false + end end + return dim end -local function interhangulskip (head,curr,currfont,prevfont,was_penalty) - local width = 0 - local interhangul = get_font_feature(currfont, "interhangul") - if interhangul and currfont == prevfont then - width = tex_sp(interhangul) - end - if not was_penalty then - d_insert_before(head,curr,d_get_penaltynode(50)) +local function insert_glue_before (head, curr, par, br, brb, classic, ict, dim) + 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 - d_insert_before(head,curr,d_make_luako_glue(width, emsize*0.04, emsize*0.02)) -end -local function interhanjaskip (head,curr,was_penalty) - if not was_penalty then - d_insert_before(head,curr,d_get_penaltynode(50)) + dim = dim or 0 + local gl = nodenew(glueid) + local en = get_en_size(curr.font) + if ict then + en = classic and en or en/4 + setglue(gl, en * ict[1] + dim, nil, en * ict[2]) + else + setglue(gl, dim, en * stretch_f, en * shrink_f) end - d_insert_before(head,curr,d_make_luako_glue(0, emsize*0.04, emsize*0.02)) -end -local function koreanlatinskip (head,curr,currfont,prevfont,was_penalty) - local width = 0 -- default: 0em - if (d_has_attribute(curr,finemathattr) or 0) > 0 then -- not ttfamily - local latincjk = get_font_feature(currfont, "interlatincjk") - if not latincjk then - latincjk = get_font_feature(prevfont, "interlatincjk") - end - if latincjk then - width = tex_sp(latincjk) - end - end - if not was_penalty then - d_insert_before(head,curr,d_get_penaltynode(50)) - end - d_insert_before(head,curr,d_make_luako_glue(width, emsize*0.04, emsize*0.02)) + head = insert_before(head, curr, pn) + return insert_before(head, curr, gl) end -local function cjk_insert_nodes(head,curr,currchar,currfont,prevchar,prevfont,was_penalty) - local currentcjtype = d_has_attribute(curr,cjtypesetattr) - local p = get_cjk_class(prevchar, currentcjtype) - local c = get_cjk_class(currchar, currentcjtype) - ---[[raise latin puncts - if d_getid(curr) == glyphnode and (d_has_attribute(curr,finemathattr) or 0) > 0 and c < 10 then -- not ttfamily - local nn, raise = d_getnext(curr), nil - while nn do - local nnid = d_getid(nn) - if nnid == glyphnode and latin_fullstop[d_getchar(nn)] then - if not raise then - raise = get_font_feature(currfont, "punctraise") - raise = raise and tex_sp(raise) - end - if raise then - local yoff = d_getfield(nn,"yoffset") or 0 - d_setfield(nn,"yoffset", yoff + raise) - end - nn = d_getnext(nn) - elseif nnid == kernnode then - nn = d_getnext(nn) - else - break - end - end - end - --raise latin puncts]] - if c == 5 and p == 5 then -- skip ------ ...... - return currchar,currfont - end - if p and p < 10 and prebreakpenalty[currchar] then - was_penalty = true - d_insert_before(head,curr,d_get_penaltynode(prebreakpenalty[currchar])) - elseif c and c < 10 and postbreakpenalty[prevchar] then - was_penalty = true - d_insert_before(head,curr,d_get_penaltynode(postbreakpenalty[prevchar])) - end - ---[[ kern is a breakpoint if followed by a glue: protrusion and compress_fullwidth_punctuations - if (c and c == 1) or (p and p >= 2 and p <= 6) then was_penalty = true end - --]] - if p and c then - if currentcjtype then - if cjk_glue_spec[p] and cjk_glue_spec[p][c] then - local width = emsize * cjk_glue_spec[p][c][1] - local stretch = 0 - local shrink = emsize * cjk_glue_spec[p][c][2] - d_insert_before(head,curr,d_make_luako_glue(width, stretch, shrink)) - elseif p < 10 and c < 9 then -- break between chosong and chosong - kanjiskip(head,curr) - elseif (p < 10 and c == 10) or (p == 10 and c < 10) then - if xspcode[currchar] then - if xspcode[currchar] % 2 == 1 then - xkanjiskip(head,curr) - end - elseif xspcode[prevchar] then - if xspcode[prevchar] > 1 then - xkanjiskip(head,curr) - end - elseif inhibitxspcode[currchar] then -- 3, 2 - if inhibitxspcode[currchar] > 1 then - xkanjiskip(head,curr) - end - elseif inhibitxspcode[prevchar] then -- 3, 1 - if inhibitxspcode[prevchar] % 2 == 1 then - xkanjiskip(head,curr) - end - else - xkanjiskip(head,curr) - end - end - else - if (p < 10 and c == 10) or (p == 10 and c < 10) then - if xspcode[currchar] then - if xspcode[currchar] % 2 == 1 then - koreanlatinskip(head,curr,currfont,prevfont,was_penalty) - end - elseif xspcode[prevchar] then - if xspcode[prevchar] > 1 then - koreanlatinskip(head,curr,currfont,prevfont,was_penalty) - end - elseif inhibitxspcode[currchar] then -- 3, 2 - if inhibitxspcode[currchar] > 1 then - koreanlatinskip(head,curr,currfont,prevfont,was_penalty) - end - elseif inhibitxspcode[prevchar] then -- 3, 1 - if inhibitxspcode[prevchar] % 2 == 1 then - koreanlatinskip(head,curr,currfont,prevfont,was_penalty) - end - else - koreanlatinskip(head,curr,currfont,prevfont,was_penalty) - end - elseif cjk_glue_spec[p] and cjk_glue_spec[p][c] then - koreanlatinskip(head,curr,currfont,prevfont,was_penalty) - elseif p == 7 and c == 7 then - interhangulskip(head,curr,currfont,prevfont,was_penalty) - elseif p < 10 and c < 9 then -- break between chosong and chosong - interhanjaskip(head,curr,was_penalty) - end +local function maybe_linebreak (head, curr, pc, pcl, cc, gomun, fid, par) + local ccl = get_char_class(cc, gomun) + 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, gomun, ict, dim) end end - return currchar,currfont + return head, cc, ccl end -local function cjk_spacing_linebreak (head) - local prevchar,prevfont,was_penalty,prevfine = nil,nil,nil,nil - local curr = head +local function process_linebreak (head, par) + local curr, pc, pcl = head, false, 0 while curr do - local currfine = d_has_attribute(curr,finemathattr) - if currfine then - local currid = d_getid(curr) - if currid == gluenode then - prevchar,prevfont,prevfine = nil,nil,nil - d_unset_attribute(curr,finemathattr) - elseif currid == glyphnode then - local currfont = d_getfont(curr) - emsize = get_font_emsize(currfont) - local uni = d_get_unicode_char(curr) - if uni and not is_unicode_vs(uni) then -- bypass VS - if prevfine and prevfine > 0 and currfine == 0 then - d_set_attribute(curr, finemathattr, 1) - end - prevchar,prevfont = cjk_insert_nodes(head,curr,uni,currfont,prevchar,prevfont,was_penalty) - prevfine = currfine - end - d_unset_attribute(curr,finemathattr) - elseif currid == mathnode then - local currchar = 0 - local currsurround, currsubtype = d_getfield(curr,"surround"), d_getsubtype(curr) - if currsurround and currsurround > 0 then - currchar = 0x4E00 - end - if currsubtype == 0 then - cjk_insert_nodes(head,curr,currchar,nil,prevchar,prevfont,was_penalty) - curr = d_end_of_math(curr) - if not curr then break end - prevchar,prevfont,prevfine = currchar,nil,nil - end - d_unset_attribute(curr,finemathattr) - elseif currid == hlistnode or currid == vlistnode then - local firstchr, firstfid = d_get_hlist_char_first(curr) - if firstchr then - cjk_insert_nodes(head,curr,firstchr,firstfid,prevchar,prevfont,was_penalty) - end - prevchar,prevfont = d_get_hlist_char_last(curr,prevchar,prevfont) - if d_getlist(curr) then prevfine = nil end -- empty box preserves prevfine. - d_unset_attribute(curr,finemathattr) - end - was_penalty = currid == penaltynode - else - if d_getid(curr) == glyphnode and prevfine and prevfine > 0 then -- fitst glyph in verb - d_set_attribute(curr, finemathattr, 1) - cjk_insert_nodes(head,curr,0,nil,prevchar,prevfont,was_penalty) - d_unset_attribute(curr,finemathattr) - prevchar,prevfont,prevfine = 0,nil,nil -- treat \verb as latin character. + local id = curr.id + if id == glyphid then + local cc = my_node_props(curr).unicode or curr.char + local gomun = has_attribute(curr, classicattr) + + head, pc, pcl = maybe_linebreak(head, curr, pc, pcl, cc, gomun, curr.font, par) + + elseif id == hlistid and has_attribute(curr, rubyattr) then + local cc, fi = ruby_char_font(curr) -- rubybase + local gomun = has_attribute(curr, classicattr) + head, pc, pcl = maybe_linebreak(head, curr, pc, pcl, cc, gomun, fi, par) + + elseif id == whatsitid and curr.mode == directmode then + local glyf, cc, fin = get_actualtext(curr) + if cc and fin and glyf then + local gomun = has_attribute(glyf, classicattr) + head = maybe_linebreak(head, curr, pc, pcl, cc, gomun, 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 = d_getnext(curr) + curr = getnext(curr) end + return head end ------------------------------------- --- remove japanese/chinese spaceskip ------------------------------------- -local function remove_cj_spaceskip (head) - local curr, prevfont = head, nil +-- compress punctuations + +local function process_glyph_width (head) + local curr = head while curr do - local currid,currsubtype = d_getid(curr), d_getsubtype(curr) - if currid == mathnode and currsubtype == 0 then - curr = d_end_of_math(curr) - elseif currid == gluenode and currsubtype == glue_type_space then -- do not touch on xspaceskip for now - local cjattr = d_has_attribute(curr,cjtypesetattr) - local prv, nxt = d_getprev(curr), d_getnext(curr) - if cjattr and cjattr > 0 and prv and nxt then - local prevclass, prevchar, nextclass - local prvid, nxtid = d_getid(prv), d_getid(nxt) - if prvid == hlistnode or prvid == vlistnode then - prevclass = get_cjk_class(d_get_hlist_char_last(prv), cjattr) - else - -- what is this strange kern before \text?? - if prvid == kernnode and d_getfield(prv,"kern") == 0 then - prv = d_getprev(prv) + 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 gomun = has_attribute(curr, classicattr) + local class = get_char_class(cc, gomun) + if (gomun or cc < 0x2000 or cc > 0x202F) -- exclude general puncts + and class >= 1 and class <= 4 then + + local gpos + if class ~= 1 then + gpos = getnext(curr) + else + gpos = getprev(curr) end - if prvid == glyphnode then - prevchar, prevfont = d_getchar(prv), d_getfont(prv) - if is_unicode_vs(prevchar) then prv = d_getprev(prv) end -- bypass VS - prevclass = get_cjk_class(d_get_unicode_char(prv), cjattr) + 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 - if nxtid == glyphnode then - nextclass = get_cjk_class(d_get_unicode_char(nxt), cjattr) - elseif nxtid == hlistnode or nxtid == vlistnode then - nextclass = get_cjk_class(d_get_hlist_char_first(nxt), cjattr) - end - if (prevclass and prevclass < 10) or (nextclass and nextclass < 10) then - d_remove_node(head,curr) - d_nodefree(curr) - curr = nxt - end end + elseif id == mathid then + curr = end_of_math(curr) end - curr = d_getnext(curr) + curr = getnext(curr) end + return head end ----------------------------------- --- compress fullwidth punctuations ----------------------------------- -local function check_next_halt_kern (head, curr) - -- glyph halt_kern glue -> glyph halt_kern rule glue - local nn = d_getnext(curr) - if nn and d_getid(nn) == kernnode and d_getnext(nn) then - d_insert_after(head, nn, d_get_rulenode(0)) - end +-- interhangul & interlatincjk + +local function is_cjk (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 check_prev_halt_kern (head, curr) - local pn = d_getprev(curr) - if pn and d_getid(pn) == kernnode and d_getprev(pn) then - -- glue halt_kern glyph -> glue rule halt_kern glyph - head = d_insert_before(head, pn, d_get_rulenode(0)) - elseif pn and d_getid(pn) == gluenode and d_has_attribute(pn, luakoglueattr) then - -- halt_kern ko_glue glyph -> ko_glue rule halt_kern glyph - local ppn = d_getprev(pn) - if d_getid(ppn) == kernnode then - head = d_remove_node(head, ppn) - head = d_insert_before(head, curr, ppn) - head = d_insert_before(head, ppn, d_get_rulenode(0)) +local function do_interhangul_option (head, curr, pc, c, fontid, par) + local cc = (is_hangul(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) end end - return head + + return head, cc end -local function compress_fullwidth_punctuations (head) - for curr in d_traverse_id(glyphnode,head) do - local currfont, currchar = d_getfont(curr), d_getchar(curr) - local cjtype = d_has_attribute(curr, cjtypesetattr) - local uni = d_get_unicode_char(curr) - local class = uni and get_cjk_class(uni, cjtype or 0) -- include U+201C ... - local chr = get_font_char(currfont, currchar) - if chr and class and class >= 1 and class <= 4 then - local halt_on = get_font_feature(currfont,'halt') or get_font_feature(currfont,'vhal') - local width = d_getfield(curr,"width") or 655360 - emsize = get_font_emsize(currfont) - local ensize = emsize/2 - local oneoften = emsize/10 - local bbox = get_char_boundingbox(currfont, currchar) - bbox = bbox or {ensize-oneoften, ensize-oneoften, ensize+oneoften, ensize+oneoften} - if class == 2 or class == 4 then - local wd - if get_font_feature(currfont,'vertical') then - wd = ensize<width and ensize-width or 0 - else - wd = (bbox[3] < ensize) and ensize-width or bbox[3]-width+oneoften - if not cjtype and wd > 0 then wd = 0 end - end - if chr.right_protruding then - -- kern is a breakpoint if followed by a glue - if not halt_on then - d_insert_after(head, curr, d_get_kernnode(wd)) - end - else - if halt_on then - check_next_halt_kern(head, curr) - else - d_insert_after(head, curr, d_get_rulenode(wd)) - end - end - elseif class == 1 then - local wd - if get_font_feature(currfont,'vertical') then - wd = ensize<width and ensize-width or 0 - else - wd = (width-bbox[1] < ensize) and ensize-width or -bbox[1]+oneoften - if not cjtype and wd > 0 then wd = 0 end - end - if chr.left_protruding then - if not halt_on then - head = d_insert_before(head, curr, d_get_kernnode(wd)) - end - else - if halt_on then - head = check_prev_halt_kern(head, curr) - else - head = d_insert_before(head, curr, d_get_rulenode(wd)) - end - end - elseif class == 3 then - local lwd, rwd - local quarter, thirdquarter, halfwd = ensize/2, ensize*1.5, width/2 - if get_font_feature(currfont,'vertical') then - rwd = quarter<halfwd and quarter-halfwd or 0 - lwd = rwd - else - rwd = (bbox[3] < thirdquarter) and quarter-halfwd or bbox[3]-width - lwd = (width-bbox[1] < thirdquarter) and quarter-halfwd or -bbox[1] - end - if chr.left_protruding then - if not halt_on then - head = d_insert_before(head, curr, d_get_kernnode(lwd)) - end - else - if halt_on then -- there's already penalty 10000 - head = check_prev_halt_kern(head, curr) - else - head = d_insert_before(head, curr, d_get_rulenode(lwd)) - end - end - if chr.right_protruding then - if not halt_on then - d_insert_after (head, curr, d_get_kernnode(rwd)) - end - else - if halt_on then - check_next_halt_kern(head, curr) - else - d_insert_after (head, curr, d_get_rulenode(rwd)) - end - 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 + 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 - end - return head -- should be returned! -end ------------------------------- --- ruby: pre-linebreak routine ------------------------------- -local function spread_ruby_box(head,extrawidth) - local to_free = {} - for curr in d_traverse_id(gluenode,head) do - local wd = d_getglue(curr) or 0 - if d_has_attribute(curr,luakoglueattr) then - d_setglue(curr, wd + extrawidth) - else - head = d_insert_before(head,curr,d_get_kernnode(wd+extrawidth)) - head = d_remove_node(head,curr) - to_free[#to_free + 1] = curr + elseif id == hlistid and has_attribute(curr, rubyattr) then + local c, fi = ruby_char_font(curr) + head, pc = do_interhangul_option(head, curr, pc, c, fi, par) + + 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 - end - if #to_free > 0 then - for _,v in ipairs(to_free) do d_nodefree(v) end + curr = getnext(curr) end return head end -local function spread_ruby_base_box (head) - for curr in d_traverse_id(hlistnode,head) do - local attr = d_has_attribute(curr,luakorubyattr) - local rubyoverlap = attr and rubynode[attr][3] - if attr and not rubyoverlap then - local ruby = d_todirect(rubynode[attr][1]) - local currwidth, rubywidth = d_getfield(curr,"width"), d_getfield(ruby,"width") - if ruby and rubywidth > currwidth then - local basehead = d_getlist(curr) - local numofglues = d_nodecount(gluenode,basehead) - local extrawidth = (rubywidth - currwidth)/(numofglues + 1) - if numofglues > 0 then - basehead = spread_ruby_box(basehead,extrawidth) - end - local leading = d_get_kernnode(extrawidth/2) - d_setfield(curr,"width", rubywidth) - d_setnext(leading, basehead) - d_setlist(curr, leading) - end +local function do_interlatincjk_option (head, curr, pc, pf, c, cf, par) + local cc = (is_cjk(c) and 1) or (is_noncjkletter(c) and 2) or 0 + local brb = cc == 2 or breakable_before[c] -- numletter != br_before + + if brb and cc*pc == 2 and curr.lang ~= nohyphen then + local fontid = cc == 1 and cf or pf + local dim = get_font_opt_dimen(fontid, "interlatincjk") + if dim then + head = insert_glue_before(head, curr, par, true, brb, false, false, dim) end end + + return head, cc, cf end -local function get_ruby_side_width (basewidth,rubywidth,adjacent) - local width,margin = (rubywidth-basewidth)/2, 0 - if adjacent then - if d_getid(adjacent) == glyphnode then - local uni = d_get_unicode_char(adjacent) - if is_unicode_vs(uni) then -- bypass VS - uni = d_get_unicode_char(d_getprev(adjacent)) +local function process_interlatincjk (head, par) + local curr, pc, pf = head, 0, 0 + while curr do + local id = curr.id + if id == glyphid then + local c = my_node_props(curr).unicode or curr.char + head, pc, pf = do_interlatincjk_option(head, curr, pc, pf, c, curr.font, par) + pc = breakable_after[c] and pc or 0 + + elseif id == hlistid and has_attribute(curr, rubyattr) then + local c, cf = ruby_char_font(curr) + head, pc, pf = do_interlatincjk_option(head, curr, pc, pf, c, cf, par) + + elseif id == whatsitid and curr.mode == directmode then + local glyf, c = get_actualtext(curr) + if c and glyf then + head, pc, pf = do_interlatincjk_option(head, curr, pc, pf, c, glyf.font, par) + curr = goto_end_actualtext(curr) end - if not is_hanja(uni) then - width = (rubywidth-basewidth-emsize)/2 - if width > 0 then - margin = emsize/2 - else - width,margin = 0,(rubywidth-basewidth)/2 - end + + elseif id == mathid then + if pc == 1 then + head = do_interlatincjk_option(head, curr, pc, pf, 0x30, pf, par) end - end - end - return width,margin -end + pc, curr = 2, end_of_math(curr) -local function get_ruby_side_kern (head) - for curr in d_traverse_id(hlistnode,head) do - local attr = d_has_attribute(curr,luakorubyattr) - local rubyoverlap = attr and rubynode[attr][3] - if rubyoverlap then - local currwidth = d_getfield(curr,"width") - local basewidth = currwidth - local ruby = d_todirect(rubynode[attr][1]) - local rubywidth = d_getfield(ruby,"width") - if currwidth < rubywidth then - local _,fid = d_get_hlist_char_first(curr) - emsize = get_font_emsize(fid) - local leftwidth,leftmargin = get_ruby_side_width(basewidth,rubywidth,d_getprev(curr)) - if leftwidth > 0 then - currwidth = currwidth + leftwidth - d_setfield(curr,"width", currwidth) - end - if leftmargin > 0 then - rubynode[attr].leftmargin = leftmargin - end - local rightwidth,rightmargin = get_ruby_side_width(basewidth,rubywidth,d_getnext(curr)) - if rightwidth > 0 then - currwidth = currwidth + rightwidth - d_setfield(curr,"width", currwidth) - end - if rightmargin > 0 then - rubynode[attr].rightmargin = rightmargin - end - rubynode[attr].rightshift = rightwidth - leftwidth - local totalspace = leftwidth+rightwidth - if totalspace > 0 then - local currhead = d_getlist(curr) - local numofglues = d_nodecount(gluenode,currhead) - local extrawidth = totalspace/(numofglues + 1) - if numofglues > 0 then - currhead = spread_ruby_box(currhead,extrawidth) - end - local leading = d_get_kernnode(extrawidth*(leftwidth/totalspace)) - d_setnext(leading, currhead) - d_setlist(curr, leading) - end + elseif id == dirid then + if pc == 1 and curr.dir:sub(1,1) == "+" then + head = do_interlatincjk_option(head, curr, pc, pf, 0x30, pf, par) + pc = 0 end - end - end -end -local function zero_width_rule_with_dir (head,curr,before) - local rule = d_get_rulenode(0) - d_setfield(rule,"dir", d_getfield(curr,"dir")) - if before then - head = d_insert_before(head,curr,rule) - else - d_insert_after(head,curr,rule) + elseif is_blocking_node(curr) then + pc = 0 + end + curr = getnext(curr) end return head end -local function no_ruby_at_margin(head) - for curr in d_traverse_id(hlistnode,head) do - local attr = d_has_attribute(curr,luakorubyattr) - if attr then - local margin = rubynode[attr].leftmargin - if margin then - head = d_insert_before(head,curr,d_get_kernnode(-margin)) - head = zero_width_rule_with_dir(head,curr,true) -- before - head = d_insert_before(head,curr,d_get_kernnode(margin)) - end - margin = rubynode[attr].rightmargin - if margin then - local nn = d_getnext(curr) - if nn then - local nnid = d_getid(nn) - if nnid == gluenode then - d_insert_after(head,curr,d_get_kernnode(-margin)) - head = zero_width_rule_with_dir(head,curr) - d_insert_after(head,curr,d_get_kernnode(margin)) - elseif nnid == penaltynode and d_getfield(nn,"penalty") < 10000 then - d_insert_after(head,nn,d_get_kernnode(-margin)) - head = zero_width_rule_with_dir(head,curr) - d_insert_after(head,curr,d_get_kernnode(margin)) +-- 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 vid = v.id + if vid ~= whatsitid -- skip whatsit or kern except userkern + and ( vid ~= kernid or v.subtype == userkern ) then + + local vchar, vfont + if vid == glyphid and v.lang ~= nohyphen then + vchar, vfont = v.char, v.font + elseif vid == hlistid and has_attribute(v, rubyattr) then + vchar, vfont = ruby_char_font(v) + end + if vchar and vfont and option_in_font(vfont, opt_name) then + ok = is_cjk(vchar) + end + + break + end + v = k == "p" and getprev(v) or getnext(v) + end + if ok then + head = noderemove(head, curr) + tableinsert(to_free, 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 ------------------------------- --- discourage character orphan ------------------------------- -local function inject_char_widow_penalty (head,curr,uni,cjattr) - if uni and prebreakpenalty[uni] ~= 10000 then - local class = get_cjk_class(uni, cjattr) - if class and class < 9 then - local pv = cjattr and 500 or 5000 - local np = d_getprev(curr) - if np and d_getid(np) == rulenode then - curr = np; np = d_getprev(curr) - end - if np and d_getid(np) == gluenode then - curr = np; np = d_getprev(curr) +-- 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_noncjkletter(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 - if np and d_getid(np) == penaltynode then - curr = np - local currpenalty = d_getfield(curr,"penalty") - if currpenalty < pv and currpenalty > 0 then -- bypass \\ case - d_setfield(curr,"penalty", pv) - end - elseif curr and d_getid(curr) == gluenode then - head, curr = d_insert_before(head,curr,d_get_penaltynode(pv)) + 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 curr + return josacode, parenlevel end -local function discourage_char_widow (head,curr) +local function process_josa (head) + local curr, tofree = head, {} while curr do - if curr == head then return end - local currid = d_getid(curr) - if currid == glyphnode then - emsize = get_font_emsize(d_getfont(curr)) - local remwd = d_nodedimensions(curr) - if remwd > 2*emsize then return end - local cjattr = d_has_attribute(curr,cjtypesetattr) - local uni = d_get_unicode_char(curr) - curr = inject_char_widow_penalty(head,curr,uni,cjattr) - elseif currid == hlistnode and currid == vlistnode then - local remwd = d_nodedimensions(curr) - local uni,fid = d_get_hlist_char_first(curr) - emsize = get_font_emsize(fid) - if remwd > 2*emsize then return end - local cjattr = d_has_attribute(curr,cjtypesetattr) - curr = inject_char_widow_penalty(head,curr,uni,cjattr) + 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) + tableinsert(tofree, curr) + end + end + unset_attribute(curr, autojosaattr) + end + elseif id == mathid then + curr = end_of_math(curr) end - curr = d_getprev(curr) + curr = getnext(curr) end + for _,v in ipairs(tofree) do nodefree(v) end + return head end ---------------------------- --- automatic josa selection ---------------------------- -local function syllable2jamo (code) - local code = code - 0xAC00 - local L = 0x1100 + mathfloor(code / 588) - local V = 0x1161 + mathfloor((code % 588) / 28) - local T = 0x11A7 + code % 28 - if T == 0x11A7 then T = nil end - return L, V, T -end +-- dotemph -local function number2josacode (n) - n = n % 10 + stringbyte("0") - if josa_code[n] then return josa_code[n] end - return nil -- 2 -end +local dotemphbox = {} +luatexko.dotemphbox = dotemphbox -local function latin2josacode (n) - n = n + stringbyte("a") - if josa_code[n] then return josa_code[n] end - return nil -- 2 +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_hanja(c) or is_chosong(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 -for c = 0x2160, 0x216B do -- Ⅰ - josa_code[c] = number2josacode(c - 0x215F) -end -for c = 0x2170, 0x217B do -- ⅰ - josa_code[c] = number2josacode(c - 0x216F) -end -for c = 0x2460, 0x2473 do -- ① - josa_code[c] = number2josacode(c - 0x245F) -end -for c = 0x2474, 0x2487 do -- ⑴ - josa_code[c] = number2josacode(c - 0x2473) -end -for c = 0x2488, 0x249B do -- ⒈ - josa_code[c] = number2josacode(c - 0x2487) -end -for c = 0x249C, 0x24B5 do -- ⒜ - josa_code[c] = latin2josacode(c - 0x249C) -end -for c = 0x24B6, 0x24CF do -- Ⓐ - josa_code[c] = latin2josacode(c - 0x24B6) -end -for c = 0x24D0, 0x24E9 do -- ⓐ - josa_code[c] = latin2josacode(c - 0x24D0) -end -for c = 0x3131, 0x314E do -- ㄱ - josa_code[c] = 3 -end -josa_code[0x3139] = 1 -- ㄹ -for c = 0x3165, 0x3186 do - josa_code[c] = 3 -end -for c = 0x3200, 0x320D do -- ㈀ - josa_code[c] = 3 -end -josa_code[0x3203] = 1 -- ㈃ -for c = 0x3260, 0x327F do -- ㉠ - josa_code[c] = 3 -end -josa_code[0x3263] = 1 -- ㉣ -for c = 0xFF10, 0xFF19 do -- 0 - josa_code[c] = number2josacode(c - 0xFF10) -end -for c = 0xFF21, 0xFF3A do -- A - josa_code[c] = latin2josacode(c - 0xFF21) -end -for c = 0xFF41, 0xFF5A do -- a - josa_code[c] = latin2josacode(c - 0xFF41) -end +-- uline -local function jamo2josacode(code) - if code and code > 0x11A7 then - if code == 0x11AF then return 1 end - return 3 - end - return 2 -end +local uline_f, uline_id = new_user_whatsit("uline","luatexko") +local no_uline_id = new_user_whatsit_id("no_uline","luatexko") -local function get_hanja_hangul_table (table,file,init) - local i = 0 - local file = kpse_find_file(file) - if not file then return table end - file = io.open(file, "r") - if not file then return table end - while true do - local d = file:read("*number") - if not d then break end - table[init + i] = d - i = i + 1 +local function ulboundary (i, n) + local what = uline_f() + if n then + what.type = lua_value -- table + what.value = { i, nodecopy(n) } + else + what.type = lua_number + what.value = i end - file:close() - return table + nodewrite(what) end +luatexko.ulboundary = ulboundary -local hanja2hangul -local function initialize_hanja2hangul () - hanja2hangul = {} - hanja2hangul = get_hanja_hangul_table(hanja2hangul,"hanja_hangul.tab",0x4E00) - hanja2hangul = get_hanja_hangul_table(hanja2hangul,"hanjaexa_hangul.tab",0x3400) - hanja2hangul = get_hanja_hangul_table(hanja2hangul,"hanjacom_hangul.tab",0xF900) -end +local white_nodes = { + [glueid] = true, + [penaltyid] = true, + [kernid] = true, + [whatsitid] = true, +} --- 1 : 리을, 2 : 중성, 3 : 종성 -local function get_josacode (prevs) - local code = prevs[1] -- last char - if not code then return 2 end - if is_hangul(code) then -- hangul syllable - local _, _, T = syllable2jamo(code) - return jamo2josacode(T) - end - if is_jungjongsong(code) then return jamo2josacode(code) end - if (code >= 0x3400 and code <= 0x9FA5) - or (code >= 0xF900 and code <= 0xFA2D) then - if not hanja2hangul then initialize_hanja2hangul() end - local _, _, T = syllable2jamo(hanja2hangul[code]) - return jamo2josacode(T) - end - -- latin - if prevs[1] < 0x80 - and prevs[2] and prevs[2] < 0x80 - and prevs[3] and prevs[3] < 0x80 then - local liii = stringchar(prevs[3], prevs[2], prevs[1]) - if josa_code[liii] then return josa_code[liii] end +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 = t.start or head, t.list + start = skip_white_nodes(start, true) + nodeslide(start) -- to get correct getprev. + curr = skip_white_nodes(curr) + if getnext(curr) then curr = getnext(curr) end + 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 = xleaders + 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 - if prevs[1] < 0x80 - and prevs[2] and prevs[2] < 0x80 then - local lii = stringchar(prevs[2], prevs[1]) - if josa_code[lii] then return josa_code[lii] end - end - if josa_code[code] then return josa_code[code] end - return 2 + return head end -local function get_josaprevs(curr,josaprev,ignoreparens,halt) - if type(halt) ~= "number" then halt = 0 end +local function process_uline (head, parent, ulitems, level) + local curr, ulitems, level = head, ulitems or {}, level or 0 while curr do - local currid = d_getid(curr) - if currid == glyphnode then - local chr = d_get_unicode_char(curr) - -- ignore chars inside parentheses (KTS workshop 2013.11.09) - if ignoreparens and chr == 0x29 then -- right parenthesis - halt = halt + 1 - elseif ignoreparens and chr == 0x28 then -- left parenthesis - halt = halt - 1 - elseif xspcode[chr] - or inhibitxspcode[chr] - or prebreakpenalty[chr] - or postbreakpenalty[chr] - or is_unicode_vs(chr) -- bypass VS - or chr == 0x302E -- tone mark - or chr == 0x302F then -- tone mark - --skip - elseif halt <= 0 then - josaprev[#josaprev + 1] = chr + 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 = value[1], value[2] + ulitems[count] = { start = curr, list = list, level = level } + elseif ulitems[value] then + head = draw_uline(head, curr, parent, ulitems[value], true) + ulitems[value] = nil end - elseif currid == hlistnode or currid == vlistnode then - josaprev = get_josaprevs(d_nodetail(d_getlist(curr)),josaprev,ignoreparens,halt) - end - if #josaprev == 3 then break end - curr = d_getprev(curr) - end - return josaprev -end -local function korean_autojosa (head) - local to_free = {} - for curr in d_traverse_id(glyphnode,head) do - local josaattr = d_has_attribute(curr,autojosaattr) - if josaattr and d_has_attribute(curr,finemathattr) then - local ignoreparens = josaattr > 1 and true or false - local josaprev = {} - josaprev = get_josaprevs(d_getprev(curr),josaprev,ignoreparens) - local josacode = get_josacode(josaprev) - local thischar = d_get_unicode_char(curr) - if thischar == 0xC774 then - local nn, nc = d_getnext(curr) - if nn and d_getid(nn) == glyphnode then - nc = d_get_unicode_char(nn) - end - if nc and nc >= 0xAC00 and nc <= 0xD7A3 then - d_setchar(curr, josa_list[0xC774][josacode]) - else - d_setchar(curr, josa_list[0xAC00][josacode]) - end - elseif thischar and josa_list[thischar] then - d_setchar(curr, josa_list[thischar][josacode]) + curr.user_id = no_uline_id -- avoid multiple run + elseif id == hlistid then + local list = curr.list + if list then + curr.list, ulitems = process_uline(list, curr, ulitems, level+1) end end - if d_getchar(curr) < 0 then - d_remove_node(head,curr) - to_free[#to_free + 1] = curr - end - end - if #to_free > 0 then - for _,v in ipairs(to_free) do d_nodefree(v) end + curr = getnext(curr) end -end - ------------------------------- --- switch to hangul/hanja font ------------------------------- -local function hangulspaceskip (engfont, hfontid, glue) - local eng = engfont.parameters - if not eng then return end - local gsp, gst, gsh, gsto, gsho = d_getglue(glue) - if gsto ~= 0 or gsho ~= 0 then return end - local esp, est, esh = eng.space, eng.space_stretch, eng.space_shrink - esp = esp and tex_round(esp) - est = est and tex_round(est) - esh = esh and tex_round(esh) - if esp == gsp and est == gst and esh == gsh then else return end - local hf = get_font_table(hfontid) --- if hf and hf.encodingbytes == 2 then else return end - return gsp, hf -end -local function virtual_unifont (prefix,size) - local data = { - name = prefix.."vf", - type = "virtual", - fonts = {}, - characters = {}, - } - local fonts = data.fonts - local chars = data.characters - for index = 0, 255 do - local name = string.format(prefix.."%02x", index) - if kpse_find_file(name,"tfm") then - local tfm = font.read_tfm(name, size) - data.parameters = data.parameters or tfm.parameters - fonts[#fonts+1] = { name=name, size=size } - local fid = #fonts - local zro = 256 * index - for i,v in pairs(tfm.characters) do - local slot = zro + i - chars[slot] = v - chars[slot].commands = { { "slot", fid, i } } - end + curr = nodeslide(head) + for i, t in pairs(ulitems) do + if level == t.level then + head = draw_uline(head, curr, parent, t) + t.start = nil end end - return data + return head, ulitems end -local function nanumtype1font(curr) - local currchar, currfont = d_getchar(curr), d_getfont(curr) - if currchar > 0xFFFF then return end - local fnt_t = get_font_table(currfont) - if not fnt_t then return end - local family = (d_has_attribute(curr,finemathattr) or 0) > 1 and not is_hanja(currchar) and "nanummj" or "nanumgt" - local series = fnt_t.shared and - fnt_t.shared.rawdata and - fnt_t.shared.rawdata.metadata and - fnt_t.shared.rawdata.metadata.pfmweight - if series then - series = series > 500 and "b" or "m" - else - series = fnt_t.name:find("^cmb") and "b" or "m" - end - local shape = fnt_t.parameters.slant > 0 and "o" or "" - local prefix = stringformat("%s%s%s",family,series,shape) - local fsize = fnt_t.size or 655360 - local fspec = stringformat("%s@%d",prefix,fsize) - local fontid = type1fonts[fspec] - if fontid == nil then -- bypass false - local fontdata = virtual_unifont(prefix, fsize) - fontid = fontdata and fontdefine(fontdata) - if fontid then - type1fonts[fspec] = fontid - type1fonts[fontid] = fontdata - else - type1fonts[fspec] = false - end - end - return fontid -end +-- ruby + +local rubybox = {} +luatexko.rubybox = rubybox -local function font_substitute(head) +local function process_ruby_pre_linebreak (head) local curr = head while curr do - local currid = d_getid(curr) - if currid == glyphnode then - local currchar, currfont = d_getchar(curr), d_getfont(curr) - if currchar then - if is_unicode_vs(currchar) then -- ideograph variation selectors - local prev = d_getprev(curr) - local prevfont = d_getid(prev) == glyphnode and d_getfont(prev) - if prevfont ~= currfont then - d_setfield(curr, "font", prevfont) -- sync font - end - else - local myfontchar, forcehangul, forcehanja - local eng = currfont and get_font_table(currfont) - if eng and eng.encodingbytes and eng.encodingbytes == 2 -- exclude type1 - and hangulpunctuations[currchar] and d_has_attribute(curr, hangulpunctsattr) - and (d_has_attribute(curr, finemathattr) or 0) > 0 -- not ttfamily - and not get_font_char(currfont, 0xAC00) then -- exclude hangul font - else - if luatexko.hangulfontforhangul or luatexko.hanjafontforhanja then - local uni = d_get_unicode_char(curr) - uni = uni and get_cjk_class(uni) or 10 - if uni < 7 then - forcehanja = luatexko.hanjafontforhanja - elseif uni < 10 then - forcehangul = luatexko.hangulfontforhangul - end - end - if not forcehangul and not forcehanja then - myfontchar = get_font_char(currfont, currchar) - end - end - if not myfontchar then - local hangul = d_has_attribute(curr, hangulfntattr) - local hanja = d_has_attribute(curr, hanjafntattr) - local fallback = d_has_attribute(curr, fallbackfntattr) - local ftable = {hangul, hanja, fallback} - if forcehanja then ftable = {hanja, hangul, fallback} end - local fid - for i = 1,3 do - fid = ftable[i] - myfontchar = fid and get_font_char(fid, currchar) - if myfontchar then break end - end - if not myfontchar and not hangulpunctuations[currchar] then - fid = nanumtype1font(curr) - myfontchar = fid and get_font_char(fid, currchar) - end - if myfontchar then - d_setfield(curr,"font",fid) - --- charraise option charraise - local charraise = get_font_feature(fid, "charraise") - if charraise then - charraise = tex_sp(charraise) - local curryoffset = d_getfield(curr,"yoffset") or 0 - d_setfield(curr,"yoffset", charraise + curryoffset) - end - --- - local nxt = d_getnext(curr) - local nxtid = nxt and d_getid(nxt) - if nxtid == glyphnode and is_unicode_vs(d_getchar(nxt)) then - nxt = d_getnext(nxt) - nxtid = nxt and d_getid(nxt) - end - if eng and nxtid then - local nxtsubtype = d_getsubtype(nxt) - -- adjust next glue by hangul font space - if nxtid == gluenode and nxtsubtype == glue_type_space and get_font_char(fid,32) then - local oldwd, ft = hangulspaceskip(eng, fid, nxt) - if oldwd and ft then - local newwd = ft.space_char_width - if not newwd then - local newsp = d_tonode(d_copy_node(curr)) - newsp.char = 32 - newsp = nodes.simple_font_handler(newsp) - newwd = newsp and newsp.width - ft.space_char_width = newwd - node.free(newsp) - end - if newwd and oldwd ~= newwd then - d_setglue(nxt, newwd, newwd/2, newwd/3) - end - end - -- adjust next italic correction kern - elseif nxtid == kernnode and nxtsubtype == kern_type_itlc and d_getfield(nxt,"kern") == 0 then - local ksl = get_font_table(fid).parameters.slant - if ksl and ksl > 0 then - d_setfield(nxt,"kern", myfontchar.italic or 0) - end - end - end - end + 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 currid == mathnode then - if d_getsubtype(curr) == 0 then - curr = d_end_of_math(curr) - end - elseif currid == discnode then - local pre, post, replace = d_getdisc(curr) - if pre then font_substitute(pre) end - if post then font_substitute(post) end - if replace then font_substitute(replace) end + elseif id == mathid then + curr = end_of_math(curr) end - curr = d_getnext(curr) - end -end - ------------------------------ --- reserve unicode code point ------------------------------ -local function assign_unicode_codevalue (head) - for curr in d_traverse_id(glyphnode, head) do - d_set_attribute(curr, luakounicodeattr, d_getchar(curr)) + curr = getnext(curr) end + return head end ------------------------------ --- reorder hangul tone marks ------------------------------ -local function reorderTM (head) - local curr, todo = head, nil +local function process_ruby_post_linebreak (head) + local curr = head while curr do - local nid = d_getid(curr) - if nid == glyphnode then - local uni = d_get_unicode_char(curr) - if uni == 0x302E or uni == 0x302F then - local tmgl = get_font_char(d_getfont(curr), uni) - local tmwidth = tmgl and tmgl.width or 0 - if todo then - if tmwidth > 0 then - local tm = curr - head, curr = d_remove_node(head, curr) - d_setprev(tm, nil) -- prev might survive! - head = d_insert_before(head, todo, tm) - curr = d_getprev(curr) - end - elseif get_font_char(d_getfont(curr), 0x25CC) then - local u25CC = d_copy_node(curr) - d_setchar(u25CC, 0x25CC) - if tmwidth > 0 then - head, curr = d_insert_after(head, curr, u25CC) - else - head = d_insert_before(head, curr, u25CC) + 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 + ruby.shift = -(curr.height + ruby_t[2]) -- rubysep + head = insert_before(head, curr, ruby) end - todo = nil - elseif is_hangul(uni) or is_chosong(uni) or uni == 0x25CC then - todo = curr - elseif is_jungjongsong(uni) then + ruby_t = nil + unset_attribute(curr, rubyattr) else - todo = nil + local list = curr.list + if list then + curr.list = process_ruby_post_linebreak(list) + end end - elseif nid == kernnode then - else - todo = nil end - curr = d_getnext(curr) + curr = getnext(curr) end return head end ----------------------------------- --- add to callback : pre-linebreak ----------------------------------- - -add_to_callback("hpack_filter", function(head) - head = d_todirect(head) - assign_unicode_codevalue(head) - korean_autojosa(head) - remove_cj_spaceskip(head) - font_substitute(head) - return d_tonode(head) -end, 'luatexko.hpack_filter_first', 1) - -add_to_callback('hpack_filter', function(head) - head = d_todirect(head) - if texcount["luakorubyattrcnt"]>0 then get_ruby_side_kern(head) end - cjk_spacing_linebreak(head) - if texcount["luakorubyattrcnt"]>0 then spread_ruby_base_box(head) end - head = compress_fullwidth_punctuations(head) - -- head = no_ruby_at_margin(head) - head = reorderTM(head) - return d_tonode(head) -end, 'luatexko.hpack_filter') - -add_to_callback('pre_linebreak_filter', function(head) - head = d_todirect(head) - assign_unicode_codevalue(head) - korean_autojosa(head) - remove_cj_spaceskip(head) - font_substitute(head) - return d_tonode(head) -end, 'luatexko.pre_linebreak_filter_first', 1) - -add_to_callback('pre_linebreak_filter', function(head) - head = d_todirect(head) - if texcount["luakorubyattrcnt"]>0 then get_ruby_side_kern(head) end - cjk_spacing_linebreak(head) - if texcount["luakorubyattrcnt"]>0 then spread_ruby_base_box(head) end - head = compress_fullwidth_punctuations(head) - discourage_char_widow(head, d_nodetail(head)) - if texcount["luakorubyattrcnt"]>0 then head = no_ruby_at_margin(head) end - head = reorderTM(head) - return d_tonode(head) -end, 'luatexko.pre_linebreak_filter') - - --------------------------- --- dot emphasis (드러냄표) --------------------------- -local function after_linebreak_dotemph (head, to_free) - local outer = not to_free - to_free = to_free or {} - for curr in d_traverse(head) do - local currid = d_getid(curr) - if currid == hlistnode then -- hlist may be nested!!! - d_setlist(curr, after_linebreak_dotemph(d_getlist(curr), to_free)) - elseif currid == glyphnode then - local attr = d_has_attribute(curr,dotemphattr) - if attr and attr > 0 then - local cc = get_cjk_class(d_get_unicode_char(curr)) - if cc and (cc == 0 or cc == 7 or cc == 8) then - local basewd = d_getfield(curr,"width") or 0 - if cc == 8 then -- check next char for old hangul jung/jongseong - local nn = d_getnext(curr) - while nn do - if d_getid(nn) ~= glyphnode then break end - local uni = d_get_unicode_char(nn) - local nc = get_cjk_class(uni) - if nc and nc == 9 and uni ~= 0x302E and uni ~= 0x302F then - basewd = basewd + (d_getfield(nn,"width") or 0) - else - break - end - nn = d_getnext(nn) - end - end - local dbox = d_todirect(dotemphnode[attr]) - local d = d_copy_node(dbox) - local dwidth = d_getfield(d,"width") - local dot = d_get_kernnode(basewd/2-dwidth/2) - d_setnext(dot, d_getlist(d)) - d_setlist(d, dot) - d_setfield(d,"width", 0) - head = d_insert_before(head,curr,d) - to_free[dbox] = true - end - d_unset_attribute(curr,dotemphattr) - end - end - end - if outer then - for k in pairs(to_free) do d_nodefree(k) 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 - return head end -------------------------------- --- ruby: post-linebreak routine -------------------------------- -local function after_linebreak_ruby (head) - for curr in d_traverse_id(hlistnode,head) do - after_linebreak_ruby(d_getlist(curr)) -- hlist may be nested!!! - local attr = d_has_attribute(curr,luakorubyattr) - if attr then - local ruby = rubynode[attr] and d_todirect(rubynode[attr][1]) - if ruby then - local currwidth, rubywidth = d_getfield(curr,"width"), d_getfield(ruby,"width") - local currheight = d_getfield(curr,"height") - if rubywidth < currwidth then - local rubyhead = d_getlist(ruby) - local numofglues = d_nodecount(gluenode,rubyhead) - local extrawidth = (currwidth - rubywidth)/(numofglues + 1) - d_setfield(ruby,"width", currwidth - extrawidth/2) - if numofglues > 0 then - d_setlist(ruby, spread_ruby_box(rubyhead,extrawidth)) - end - else - local right = rubynode[attr].rightshift or 0 - d_setfield(ruby,"width", currwidth + (rubywidth-currwidth+right)/2) - end - d_setfield(ruby,"shift", -currheight-rubynode[attr][2]) - d_insert_after(head,curr,ruby) - d_insert_after(head,curr,d_get_kernnode(-d_getfield(ruby,"width"))) - end - rubynode[attr] = nil - d_unset_attribute(curr,luakorubyattr) +local function pdfliteral_direct_actual (syllable) + local data + if syllable then + local t = {} + for _,v in ipairs(syllable) do + tableinsert(t, 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 ---------------------- --- underline emphasis ---------------------- - -local uline_start_id = luatexbase.new_whatsit("luako_uline_start") -local uline_stop_id = luatexbase.new_whatsit("luako_uline_stop") -local function uline_boundary (num, list) - local n = d_nodenew(whatsitnode, user_whatsit) - d_setfield(n, "user_id", list and uline_start_id or uline_stop_id) - d_setfield(n, "type", 110) -- node list - d_setfield(n, "value", n_copy_list(list)) - d_set_attribute(n, luakoulineattr, num) - d_nodewrite(n) +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 -luatexko.uline_boundary = uline_boundary -local skippable = { - [gluenode ] = true, - [penaltynode] = true, - [kernnode ] = true, - [whatsitnode] = true, -} -local function draw_underline(head,curr,glueset,gluesign,glueorder,ubox,start) - start = start or head - while start do - if not skippable[d_getid(start)] then break end - start = d_getnext(start) - end +local function process_reorder_tonemarks (head) + local curr = head while curr do - local p = d_getprev(curr) - if not skippable[d_getid(p)] then break end - curr = p - end - local width = d_nodedimensions(glueset,gluesign,glueorder,start,curr) - if width and width > 0 then - local glue = d_get_gluenode(width) - d_setsubtype(glue, 101) -- cleaders - d_setleader(glue, d_copy_node(ubox)) - head = d_insert_before(head, start, glue) - head = d_insert_before(head, start, d_get_kernnode(-width)) - end - return head -end + 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 + tableinsert(syllable, 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) -local function after_linebreak_underline(head,glueorder,glueset,gluesign,ulstart,level) - ulstart, level = ulstart or {}, level or 0 - - for curr in d_traverse(head) do - local currid = d_getid(curr) - if currid == hlistnode then - local newhead - newhead, ulstart = after_linebreak_underline( - d_getlist(curr), - d_getfield(curr,"glue_order"), - d_getfield(curr,"glue_set"), - d_getfield(curr,"glue_sign"), - ulstart, - level+1) - d_setlist(curr, newhead) - elseif currid == whatsitnode and d_getsubtype(curr) == user_whatsit then - local attr = d_has_attribute(curr, luakoulineattr) - local whatsit_type = d_getfield(curr, "type") - if attr and whatsit_type == 110 then - local user_id = d_getfield(curr, "user_id") - if user_id == uline_start_id then - local list = d_getfield(curr, "value") - ulstart[attr] = { start = curr, list = list, level = level } - elseif user_id == uline_stop_id and ulstart[attr] then - local ubox, start = ulstart[attr].list, ulstart[attr].start - head = draw_underline(head,curr,glueset,gluesign,glueorder,ubox,start) - ulstart[attr] = nil + head = noderemove(head, fin) + head = insert_before(head, ini, fin) end - d_unset_attribute(curr,luakoulineattr) - end - end - end - local curr = d_nodetail(head) - for i, v in pairs(ulstart) do - local ubox, start = v.list, v.start - if ubox and level == v.level then - head = draw_underline(head,curr,glueset,gluesign,glueorder,ubox,start) - ulstart[i].start = nil - end - 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 - return head, ulstart -end + curr = goto_end_actualtext(curr) ------------------------------------ --- add to callback : post-linebreak ------------------------------------ -add_to_callback('vpack_filter', function(head) - head = d_todirect(head) - if texcount["luakodotemphcnt"]>0 then head = after_linebreak_dotemph(head) end - if texcount["luakorubyattrcnt"]>0 then after_linebreak_ruby(head) end - if texcount["luakoulineboxcnt"]>0 then head = after_linebreak_underline(head) end - return d_tonode(head) -end, 'luatexko.vpack_filter') - -add_to_callback('hpack_filter', function(head, groupcode) - if groupcode == "align_set" then - head = d_todirect(head) - if texcount["luakodotemphcnt"]>0 then head = after_linebreak_dotemph(head) end - if texcount["luakorubyattrcnt"]>0 then after_linebreak_ruby(head) end - if texcount["luakoulineboxcnt"]>0 then head = after_linebreak_underline(head) end - head = d_tonode(head) + elseif id == mathid then + curr = end_of_math(curr) + end + curr = getnext(curr) end return head -end, 'luatexko.hpack_filter.postlinebreak') +end +-- vertical font ------------------------------------- --- vertical typesetting: EXPERIMENTAL ------------------------------------- local streamreader = utilities.files local openfile = streamreader.open local closefile = streamreader.close @@ -1919,7 +1324,7 @@ local readfixed = streamreader.readfixed4 local readshort = streamreader.readinteger2 local setpos = streamreader.setposition -local function getotftables (f, subfont) +local function get_otf_tables (f, subfont) if f then local sfntversion = readstring(f,4) if sfntversion == "ttcf" then @@ -1953,7 +1358,7 @@ local function getotftables (f, subfont) end end -local function readmaxp (f, t) +local function read_maxp (f, t) if f and t then setpos(f, t.offset) return { @@ -1963,7 +1368,7 @@ local function readmaxp (f, t) end end -local function readvhea (f, t) +local function read_vhea (f, t) if f and t then setpos(f, t.offset) return { @@ -1988,7 +1393,7 @@ local function readvhea (f, t) end end -local function readvmtx (f, t, numofheights, numofglyphs) +local function read_vmtx (f, t, numofheights, numofglyphs) if f and t and numofheights and numofglyphs then setpos(f, t.offset) local vmtx = {} @@ -2010,96 +1415,93 @@ local function readvmtx (f, t, numofheights, numofglyphs) end end -local tsbtable +local tsb_font_data = {} local function get_tsb_table (filename, subfont) subfont = tonumber(subfont) or 1 local key = stringformat("%s::%s", filename, subfont) - if tsbtable[key] then - return tsbtable[key] + 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 = getotftables(f, subfont) + local tables = get_otf_tables(f, subfont) if tables then - local vhea = readvhea(f, tables.vhea) + local vhea = read_vhea(f, tables.vhea) local numofheights = vhea and vhea.numheights - local maxp = readmaxp(f, tables.maxp) + local maxp = read_maxp(f, tables.maxp) local numofglyphs = maxp and maxp.numglyphs - vmtx = readvmtx(f, tables.vmtx, numofheights, numofglyphs) + vmtx = read_vmtx(f, tables.vmtx, numofheights, numofglyphs) end closefile(f) - tsbtable[key] = vmtx + tsb_font_data[key] = vmtx return vmtx end end -local function cjk_vertical_font (vf) - if not vf.shared then return end - if not vf.shared.features then return end - if not vf.shared.features["vertical"] then return end - if vf.type == "virtual" then return end - - local subfont = vf.specification and vf.specification.sub - local tsbtable = get_tsb_table(vf.filename, subfont) - if not tsbtable then - warn("%s does not have vertical metric info", vf.fontname) +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.fontname) return end - local params = vf.parameters or {} - local shared = vf.shared or {} - local quad = params.quad or 655360 - local ascender = params.ascender or quad*0.8 - local descender = params.descender or quad*0.2 - local factor = params.factor or 655.36 - local xheight = params.x_height or quad/2 - local goffset = xheight/2 - quad/2 - local descriptions = shared.rawdata and shared.rawdata.descriptions - local spbp = 65536*(7227/7200) - for i,v in pairs(vf.characters) do - local dsc = descriptions[i] - local gl = v.index - local tsb_gl = tsbtable and tsbtable[gl] - local vw = tsb_gl and tsb_gl.ht; vw = vw and vw * factor or quad - local tsb = tsb_gl and tsb_gl.tsb - local bb4 = dsc and dsc.boundingbox and dsc.boundingbox[4] - local asc = bb4 and tsb and (bb4+tsb)*factor or ascender - local hw = v.width or quad - local offset = hw/2 + goffset - local vh = hw > 0 and hw or 0 - asc = asc/spbp; offset = offset/spbp + 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") + if not goffset then + goffset = (parameters.x_height or quad/2) / 2 + end + 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 = { - {'special', stringformat('pdf:q 0 1 -1 0 %.3f %.3f cm', asc, -offset)}, - {'push'}, - {'char', i}, - {'pop'}, - {'special', 'pdf:Q'}, + { "down", -voff }, + { "right", hoff }, + { "pdf", "q 0 1 -1 0 0 0 cm" }, + { "push" }, + { "char", i }, + { "pop" }, + { "pdf", "Q" }, } - v.width = vw - v.height = vh*(ascender/quad) - v.depth = vh*(descender/quad) - v.italic = nil + 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 - --- vertical gpos - local res = vf.resources or {} - if res.verticalgposhack then - return -- avoid multiple running + 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 typesetting + 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 - local cover = vv.coverage or {} - for iii,vvv in pairs(cover) do + for _,vv in pairs(v.steps or {}) do + for _,vvv in pairs(vv.coverage or {}) do if type(vvv) == "table" and #vvv == 4 then - cover[iii] = { -vvv[2], vvv[1], vvv[4], vvv[3], 0 } + 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 @@ -2107,10 +1509,10 @@ local function cjk_vertical_font (vf) for _,vv in pairs(v.steps or {}) do for _,vvv in pairs(vv.coverage or {}) do for _,vvvv in pairs(vvv) do - for iiiii,vvvvv in pairs(vvvv) do + for _,vvvvv in pairs(vvvv) do if type(vvvvv) == "table" and #vvvvv == 4 then - -- last 0 to avoid multiple round - vvvv[iiiii] = { -vvvvv[2], vvvvv[1], vvvvv[4], vvvvv[3], 0 } + vvvvv[1], vvvvv[2], vvvvv[3], vvvvv[4], vvvvv[5] = + -vvvvv[2], vvvvv[1], vvvvv[4], vvvvv[3], 0 end end end @@ -2119,72 +1521,228 @@ local function cjk_vertical_font (vf) end end end - res.verticalgposhack = true end -local otffeatures = fonts.constructors.newfeatures("otf") -otffeatures.register { - name = "vertical", - description = "vertical typesetting", - initializers = { - node = function (tfmdata,value) - if value and not tsbtable then - tsbtable = {} - add_to_callback("luaotfload.patch_font",cjk_vertical_font,"luatexko.vertical_virtual_font") +-- 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 ------------------------------------- --- no embedding ------------------------------------- -local function dontembedthisfont (tfmdata, value) - if value == "no" then - fonts.constructors.dontembed[tfmdata.properties.filename] = 1 +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 -otffeatures.register { - name = "embedding", - description = "dont embed this font", - initializers = { - base = dontembedthisfont, - node = dontembedthisfont, - } +-- 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, + }, } ------------------------------------- --- italic correction for fake-slant font ------------------------------------- -local function fakeslant_itlc (tfmdata) - local slfactor = tfmdata.parameters.slantfactor - if slfactor and slfactor > 0 then else return end - tfmdata.parameters.slant = slfactor * 65536 - local chrs = tfmdata.characters - for i,v in pairs(chrs) do - local italic = v.height * slfactor - if italic > 0 then - chrs[i].italic = italic +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 -end -add_to_callback("luaotfload.patch_font", fakeslant_itlc, "luatexko.fakeslant_itlc") - ------------------------------------- --- Actual Text ------------------------------------- -local function actualtext (str) - local t = {} - for uni in string.utfvalues(str) do - if uni < 0x10000 then - t[#t+1] = stringformat("%04X",uni) - else -- surrogate - uni = uni - 0x10000 - t[#t+1] = stringformat("%04X%04X", uni/0x400+0xD800, uni%0x400+0xDC00) + 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.fontname] 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.fontname) + active_processes[fontdata.fontname] = true end end - tex.sprint(stringformat("<FEFF%s>", table.concat(t))) + + 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 + +-- default hangul font + +local function current_has_hangul_chars (cnt) + texcount[cnt] = char_in_font(fontcurrent(), 0xAC00) and 1 or 0 end -luatexko.actualtext = actualtext +luatexko.currenthashangulchars = current_has_hangul_chars diff --git a/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty b/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty index a54b14ade60..d575a0ce1df 100644 --- a/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty +++ b/Master/texmf-dist/tex/luatex/luatexko/luatexko.sty @@ -1,7 +1,7 @@ % luatexko.sty % -% Copyright (c) 2013-2019 Dohyun Kim <nomos at ktug org> -% Soojin Nam <jsunam at gmail com> +% 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 @@ -11,450 +11,707 @@ % and version 1.3c or later is part of all distributions of LaTeX % version 2006/05/20 or later. -\ifx\luatexkocatcodeofatchar\undefined\else\endinput\fi % no multiple loading - -\begingroup\expandafter\expandafter\expandafter\endgroup -\expandafter\ifx\csname selectfont\endcsname\relax % plain tex - \edef\luatexkocatcodeofatchar{\catcode`@=\the\catcode`@} - \input luatexko-core.sty - %\hangulfont{name:NanumGothic:interlatincjk=0.125em} at 10pt - %\setmathhangulfont{NanumGothic} - \luatexkocatcodeofatchar - \expandafter\endinput -\fi - -\ProvidesPackage{luatexko}[2019/04/06 v1.24 Typesetting Korean with LuaLaTeX] - -\ifdefined\adjustspacing\else % luatex < 0.87 - \let\adjustspacing\pdfadjustspacing - \let\protrudechars\pdfprotrudechars +\ifdefined\luatexkohangulfontattr \endinput\fi +\ifdefined\selectfont + \ProvidesPackage{luatexko}[2019/05/01 v2.0 typesetting Korean with LuaTeX] + \RequirePackage{luatexbase} + \RequirePackage{fontspec} +\else + \input luatexbase.sty + \input luaotfload.sty + \chardef\luatexkoatcatcode=\catcode`\@ + \catcode`\@=11 \fi - -%% package options -\newif\if@hangul -\newif\if@hanja -\newif\if@luatexko@loadfontspec\@luatexko@loadfontspectrue -\DeclareOption{hangul}{\@hangultrue} -\DeclareOption{hanja}{\@hangultrue\@hanjatrue} -\DeclareOption{unfonts}{} -\DeclareOption{nofontspec}{\@luatexko@loadfontspecfalse} -\ProcessOptions\relax - -\if@luatexko@loadfontspec - \RequirePackage{fontspec}[2018/07/30] +\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.lua" } +% 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#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) + }#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.2ex} % 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{% base + \vrule width\z@ height 2ex depth\z@ #1}% + \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 + \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\relax + \unhbox\tw@ + \hskip\dimen@ii\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 +\protected\def\horizontal#1{% + \leavevmode + \setbox\z@\vbox\bgroup + \hsize#1\relax + \typesetmodern } +\protected\def\endhorizontal{% + \egroup + \luatexkounrotatebox\z@ + \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 - -\RequirePackage{luatexko-core} - -\if@luatexko@loadfontspec - %% fontspec Korean options - \newfontfeature{YetHangul}{script=hang} - \newfontfeature{InterHangul}{interhangul=#1} - \newfontfeature{InterLatinCJK}{interlatincjk=#1} - \newfontfeature{PunctRaise}{punctraise=#1} - \newfontfeature{CharRaise}{charraise=#1} - \newfontfeature{NoEmbed}{embedding=no} - \aliasfontfeature{NoEmbed}{NoEmbedding} - \newfontfeature{protrusion}{protrusion=#1} - \newfontfeature{expansion}{expansion=#1} - \ExplSyntaxOn - \DeclareDocumentCommand \newhangulfontfeature {mmmm} - { - \keys_define:nn { fontspec } - { - #1 .code:n = - { - \keys_set:nn { fontspec } { #2 = {##1} } #4 - }, - #1 .default:n = {#3} - } - } - \newhangulfontfeature{Protrusion}{protrusion}{default} - {\aftergroup\protrudechars\aftergroup\tw@} - \newhangulfontfeature{Expansion}{expansion}{default} - {\aftergroup\adjustspacing\aftergroup\tw@} - %% default font features - \cs_set_eq:NN \defaulthangulfontfeatures \defaultfontfeatures - \cs_set_eq:NN \defaulthanjafontfeatures \defaultfontfeatures - \cs_set_eq:NN \defaultfallbackfontfeatures \defaultfontfeatures - \defaultfontfeatures - [ - \serifhangul@font, \serifhanja@font, \seriffallback@font, - \sanshangul@font, \sanshanja@font, \sansfallback@font, - ] - {Ligatures=TeX} - %% macros for Korean fonts - \DeclareDocumentCommand \setmainhangulfont { O{} m O{} } - { - \setfontfamily \serifhangul@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \rmdefault - { - \cs_set_eq:NN \hangul@font \serifhangul@font - \luatexko@hangul@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setmainhanjafont { O{} m O{} } - { - \setfontfamily \serifhanja@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \rmdefault - { - \cs_set_eq:NN \hanja@font \serifhanja@font - \luatexko@hanja@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setmainfallbackfont { O{} m O{} } - { - \setfontfamily \seriffallback@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \rmdefault - { - \cs_set_eq:NN \fallback@font \seriffallback@font - \luatexko@fallback@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setsanshangulfont { O{} m O{} } - { - \setfontfamily \sanshangul@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \sfdefault - { - \cs_set_eq:NN \hangul@font \sanshangul@font - \luatexko@hangul@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setsanshanjafont { O{} m O{} } - { - \setfontfamily \sanshanja@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \sfdefault - { - \cs_set_eq:NN \hanja@font \sanshanja@font - \luatexko@hanja@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setsansfallbackfont { O{} m O{} } - { - \setfontfamily \sansfallback@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \sfdefault - { - \cs_set_eq:NN \fallback@font \sansfallback@font - \luatexko@fallback@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setmonohangulfont { O{} m O{} } - { - \setfontfamily \monohangul@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \ttdefault - { - \cs_set_eq:NN \hangul@font \monohangul@font - \luatexko@hangul@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setmonohanjafont { O{} m O{} } - { - \setfontfamily \monohanja@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \ttdefault - { - \cs_set_eq:NN \hanja@font \monohanja@font - \luatexko@hanja@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \setmonofallbackfont { O{} m O{} } - { - \setfontfamily \monofallback@font [#1,#3] {#2} - \str_if_eq_x:nnT \familydefault \ttdefault - { - \cs_set_eq:NN \fallback@font \monofallback@font - \luatexko@fallback@selectfont - } - \ignorespaces - } - \DeclareDocumentCommand \newhangulfontfamily { m O{} m O{} } - { - \exp_args:Nc \setfontfamily { luatexkofont@ \token_to_str:N #1 } [#2,#4] {#3} - \DeclareRobustCommand #1 - { - \cs_set_eq:Nc \hangul@font { luatexkofont@ \token_to_str:N #1 } - \luatexko@hangul@selectfont - } - } - \DeclareDocumentCommand \newhanjafontfamily { m O{} m O{} } - { - \exp_args:Nc \setfontfamily { luatexkofont@ \token_to_str:N #1 } [#2,#4] {#3} - \DeclareRobustCommand #1 - { - \cs_set_eq:Nc \hanja@font { luatexkofont@ \token_to_str:N #1 } - \luatexko@hanja@selectfont - } - } - \DeclareDocumentCommand \newfallbackfontfamily { m O{} m O{} } - { - \exp_args:Nc \setfontfamily { luatexkofont@ \token_to_str:N #1 } [#2,#4] {#3} - \DeclareRobustCommand #1 - { - \cs_set_eq:Nc \fallback@font { luatexkofont@ \token_to_str:N #1 } - \luatexko@fallback@selectfont - } - } - \DeclareDocumentCommand \newhangulfontface { m O{} m O{} } - { - \exp_args:Nc \newfontface { luatexkofont@ \token_to_str:N #1 } [#2,#4] {#3} - \DeclareRobustCommand #1 - { - \cs_set_eq:Nc \hangul@font { luatexkofont@ \token_to_str:N #1 } - \luatexko@hangul@selectfont - } - } - \DeclareDocumentCommand \newhanjafontface { m O{} m O{} } - { - \exp_args:Nc \newfontface { luatexkofont@ \token_to_str:N #1 } [#2,#4] {#3} - \DeclareRobustCommand #1 - { - \cs_set_eq:Nc \hanja@font { luatexkofont@ \token_to_str:N #1 } - \luatexko@hanja@selectfont - } - } - \DeclareDocumentCommand \newfallbackfontface { m O{} m O{} } - { - \exp_args:Nc \newfontface { luatexkofont@ \token_to_str:N #1 } [#2,#4] {#3} - \DeclareRobustCommand #1 - { - \cs_set_eq:Nc \fallback@font { luatexkofont@ \token_to_str:N #1 } - \luatexko@fallback@selectfont - } - } - \DeclareDocumentCommand \hangulfontspec { O{} m O{} } - { - \setfontfamily \hangul@font [#1,#3] {#2} - \luatexko@hangul@selectfont - \ignorespaces - } - \cs_set_eq:NN \adhochangulfont \hangulfontspec - \DeclareDocumentCommand \hanjafontspec { O{} m O{} } - { - \setfontfamily \hanja@font [#1,#3] {#2} - \luatexko@hanja@selectfont - \ignorespaces - } - \cs_set_eq:NN \adhochanjafont \hanjafontspec - \DeclareDocumentCommand \fallbackfontspec { O{} m O{} } - { - \setfontfamily \fallback@font [#1,#3] {#2} - \luatexko@fallback@selectfont - \ignorespaces - } - \cs_set_eq:NN \adhocfallbackfont \fallbackfontspec - \DeclareDocumentCommand \addhangulfontfeature { m } - { - \group_begin: - \cs_if_exist:NT \hangul@font { \hangul@font } - \addfontfeature {#1} - \use:x - { - \group_end: - \exp_not:N \tl_set:Nn \exp_not:N \hangul@font - { - \exp_not:N \fontfamily { \f@family } \exp_not:N \selectfont - } - } - \luatexko@hangul@selectfont - \ignorespaces - } - \cs_set_eq:NN \addhangulfontfeatures \addhangulfontfeature - \DeclareDocumentCommand \addhanjafontfeature { m } - { - \group_begin: - \cs_if_exist:NT \hanja@font { \hanja@font } - \addfontfeature {#1} - \use:x - { - \group_end: - \exp_not:N \tl_set:Nn \exp_not:N \hanja@font - { - \exp_not:N \fontfamily { \f@family } \exp_not:N \selectfont - } - } - \luatexko@hanja@selectfont - \ignorespaces - } - \cs_set_eq:NN \addhanjafontfeatures \addhanjafontfeature - \DeclareDocumentCommand \addfallbackfontfeature { m } - { - \group_begin: - \cs_if_exist:NT \fallback@font { \fallback@font } - \addfontfeature {#1} - \use:x - { - \group_end: - \exp_not:N \tl_set:Nn \exp_not:N \fallback@font - { - \exp_not:N \fontfamily { \f@family } \exp_not:N \selectfont - } - } - \luatexko@fallback@selectfont - \ignorespaces - } - \cs_set_eq:NN \addfallbackfontfeatures \addfallbackfontfeature - % hangul math font command - \DeclareDocumentCommand \setmathhangulfont { O{} m O{} } +% hangul normalize +\def\luatexhangulnormalize{% + \directlua{require"luatexko-normalize"}% + \afterassignment\luatexkohangulnormalize\count@} +\def\luatexkohangulnormalize{% + \ifcase\count@ \directlua{ luatexkonormalize.unload() }% 0: none + \or \directlua{ luatexkonormalize.compose() }% 1: nfc + \else \directlua{ luatexkonormalize.decompose() }% 2: nfd + \fi } +% convert uhc to utf8 +\def\luatexuhcinputencoding{% + \directlua{require"luatexko-uhc2utf8"}% + \afterassignment\luatexkouhcinputencoding\count@} +\def\luatexkouhcinputencoding{% + \ifcase\count@ \directlua{ luatexkouhc2utf8.stopconvert() }% + \else \directlua{ luatexkouhc2utf8.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 { - \fontspec_set_family:Nnn \luatexko@math@hangul@family {#1,#3} {#2} + \exp_args:Nc \fontfamily { luatexko_user_family_ \cs_to_str:N #1 } \selectfont } -%% fontspec 2018 -\RenewDocumentCommand \setmainfont { O{} m O{} } + \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 { - \__fontspec_main_setmainfont:nn {#1,#3} {#2} - \protected\edef\rmfamily{ - \unexpanded{\luatexko@serif@fonts} - \unexpanded\expandafter{\rmfamily} - } - \ignorespaces + \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 } -\RenewDocumentCommand \setsansfont { O{} m O{} } + \str_if_eq_x:nnT \familydefault \rmdefault { - \__fontspec_main_setsansfont:nn {#1,#3} {#2} - \protected\edef\sffamily{ - \unexpanded{\luatexko@sans@fonts} - \unexpanded\expandafter{\sffamily} - } - \ignorespaces + \cs_set_eq:NN \luatexkohanjafont \luatexkomainhanjafont + \luatexkohanjaselectfont } -\RenewDocumentCommand \setmonofont { O{} m O{} } + \ignorespaces +} +\DeclareDocumentCommand \setsanshanjafont { O{} m O{} } +{ + \fontspec_set_family:Nnn \luatexkosanshanjafamily { Ligatures=TeX, #1, #3 } { #2 } + \DeclareRobustCommand \luatexkosanshanjafont { - \__fontspec_main_setmonofont:nn {#1,#3} {#2} - \protected\edef\ttfamily{ - \unexpanded{\luatexko@mono@fonts} - \unexpanded\expandafter{\ttfamily} + \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 } - \ignorespaces + \luatexkohanjaselectfont } -\cs_set_eq:NN \setromanfont \setmainfont - \ExplSyntaxOff -\fi - -\RequirePackage{everysel} -\EverySelectfont{% - \luatexko@hangul@selectfont - \luatexko@hanja@selectfont - \luatexko@fallback@selectfont + \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 } -% serif : finemathattr = 2 -% sans : finemathattr = 1 -% tt : finemathattr = 0 -% verbatim : finemathattr = nil -\def\luatexko@serif@fonts{% - \let\hangul@font\serifhangul@font - \let\hanja@font\serifhanja@font - \let\fallback@font\seriffallback@font - \finemathattr\tw@ +\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 } -\def\luatexko@sans@fonts{% - \let\hangul@font\sanshangul@font - \let\hanja@font\sanshanja@font - \let\fallback@font\sansfallback@font - \finemathattr\@ne +\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 } -\def\luatexko@mono@fonts{% - \let\hangul@font\monohangul@font - \let\hanja@font\monohanja@font - \let\fallback@font\monofallback@font - \finemathattr\z@ +\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 } - -\protected\edef\rmfamily{% - \unexpanded{\luatexko@serif@fonts}\unexpanded\expandafter{\rmfamily}} -\protected\edef\sffamily{% - \unexpanded{\luatexko@sans@fonts}\unexpanded\expandafter{\sffamily}} -\protected\edef\ttfamily{% - \unexpanded{\luatexko@mono@fonts}\unexpanded\expandafter{\ttfamily}} -\edef\verbatim@font{\unexpanded\expandafter{\verbatim@font - \unsetattribute\finemathattr}} - -\AtBeginDocument{ -\unless\ifdefined\serifhangul@font\let\serifhangul@font\sanshangul@font\fi -\unless\ifdefined\sanshangul@font \let\sanshangul@font\serifhangul@font\fi -\unless\ifdefined\monohangul@font \let\monohangul@font\sanshangul@font \fi -\unless\ifdefined\serifhangul@font\unless\ifdefined\sanshangul@font - \hangulpunctuations=0 % nanumtype1 -\fi\fi -% don't forget to redefine \normalfont -\expandafter\ifx\familydefault\rmdefault - \protected\edef\normalfont{% - \unexpanded{\luatexko@serif@fonts}\unexpanded\expandafter{\normalfont}} - % and current hangul font - \luatexko@serif@fonts -\else\expandafter\ifx\familydefault\sfdefault - \protected\edef\normalfont{% - \unexpanded{\luatexko@sans@fonts}\unexpanded\expandafter{\normalfont}} - \luatexko@sans@fonts -\else - \protected\edef\normalfont{% - \unexpanded{\luatexko@mono@fonts}\unexpanded\expandafter{\normalfont}} - \luatexko@mono@fonts -\fi\fi -\let\reset@font\normalfont -% reset hangul font -\selectfont -% for hyperref -\@ifpackageloaded{hyperref}{\pdfstringdefDisableCommands{% - \let\dotemph\@firstofone - \let\ruby\@firstoftwo - \let\uline\@firstofone - \let\sout\@firstofone - \let\uuline\@firstofone - \let\xout\@firstofone - \let\uwave\@firstofone - \let\dashuline\@firstofone - \let\dotuline\@firstofone - \let\actualtext\@firstofone - \let\inhibitglue\empty - \def\는{는}% - \def\은{은}% - \def\을{을}% - \def\를{를}% - \def\와{와}% - \def\과{과}% - \def\가{가}% - \def\이{이}% - \def\라{라}% - \def\으{으}% - \def\로{로}% - }}{} +\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 } - -\if@luatexko@loadfontspec - %% hangul in math --- latex - \AtBeginDocument{ - \begingroup - \unless\ifdefined\luatexko@math@hangul@family - \ifdefined\hangul@font \hangul@font \fi - \xdef\luatexko@math@hangul@family{\f@family}% - \fi - \endgroup - \DeclareSymbolFont{mathhangul}% - \f@encoding\luatexko@math@hangul@family\mddefault\updefault - \ifcsname\f@encoding/\luatexko@math@hangul@family/\bfdefault/\updefault\endcsname - \SetSymbolFont{mathhangul}{bold}% - \f@encoding\luatexko@math@hangul@family\bfdefault\updefault - \fi +\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} } -\fi - + \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} +% 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 - \AtBeginDocument{\RequirePackage{konames-utf}} \linespread{1.3888} \footnotesep=1.3888\footnotesep \skip\footins=\glueexpr\skip\footins/72*100\relax - \hangulpunctuations=1 \frenchspacing \fi - \endinput |