《Lua-in-ConTeXt》03:兩個世界

garfileo發表於2023-02-01
上一篇:ConTeXt 計算機

ConTeXt 裡的 Lua。這句話博大精深。

ConTeXt 世界裡的大部分設施是用 TeX 語言構建的。在 ConTeXt 原始檔裡, \ 後面跟隨一個或多個英文字母的文字稱為控制序列。 \ 後面跟隨單個非字母的文字,也稱為控制序列。

例如

\starttext
foo \ConTeXt\ bar
\stoptext

其中,\starttext\ConTeXt\stoptext 以及 \空格,皆為控制序列。

所有的控制序列有著一個共同的使命,將排版資訊輸出到 PDF 檔案裡,簡而言之,即排版。例如上例,排版結果如下圖所示

每個控制序列都有其獨特的作用。例如,在 ConTeXt 的世界裡,只有位於 \starttext\stoptext 之間的內容,方有機會輸出至 PDF 檔案。

TeX 語言能夠組合既有的控制序列,派生出新的控制序列,這是 TeX 世界生生不息的根源所在。不妨從人類語言字片語合的角度理解控制序列的組合,例如「人」和「間」的組合產生了「人間」。

TeX 語言所構造的世界在 TeX 原始檔裡佔主體地位。Lua 語言通常僅出現於控制序列 \ctxlua{ ... }

\startluacode
... ... ...
\stopluacode

中省略號指示的位置,其中 \ctxlua\startluacode 以及 \stopluacode 是溝通 TeX 世界和 Lua 世界的隧道,但 Lua 世界裡透過計算產生的結果最終需要傳遞於 TeX 世界。

因此,TeX 世界看了 Lua 世界一眼說,世界是我們的,也是你們的,但歸根結底是我們的。

Lua 世界說,歸根結底是 PDF 的。

現代大多數字處理軟體,例如微軟 Word,金山 WPS,自由的 LibreOffice Writer……它們皆能將自己的排版結果轉化為 PDF 檔案。現在的網頁瀏覽器也能將網頁內容儲存為 PDF 檔案。有什麼必要使用 TeX 製作 PDF 檔案呢?

同樣是照片,手機拍出來的,跟單反相機拍出來的,通常也不是一回事。TeX 很像單反相機。

倘若對排版有所追求,TeX 就值得學習。

倘若 TeX 值得學習,ConTeXt 也就值得學習。

倘若 ConTeXt 值得學習,ConTeXt 裡的 Lua 也值得學習。因為 Lua 語言能讓 TeX 世界更容易生生不息。

TeX 語言讓 TeX 世界生生不息的方式像是一群原始人努力維護著一個火堆,以免斷了火種。當 Lua 語言出現在 TeX 世界裡時,這個火堆就變成了火柴,打火機,火焰噴射器……

為了充分說明這一點,先了解一下 \title 這個控制序列,它用於排版文章的標題。例如

\starttext
\title{Hello world!}

... ... some text ... ...

\stoptext

假設上述內容是 ConTeXt 原始檔 foo.tex 的全部內容,執行

$ context foo.tex

命令裡,可無須寫 .tex,以下命令與上述命令等價:

$ context foo

結果得到的 foo.pdf,它在我機器上的 PDF 閱讀器裡的樣子如下圖所示:

顯然,作為標題的 Hello world! 的字號要比正文 ... ... some text ... ... 大了許多,這是很合理的,因為標題需要醒目。

ConTeXt 預設的標題樣式並不是太好看,但是它允許使用者自定義標題樣式。例如,

\setuphead[title][align=middle]

\starttext
\title{Hello world}

... ... some text ... ...

\stoptext

在重新生成的 PDF 檔案裡,標題就位於頁面正中了。

也可以設定標題的顏色,例如將其設定為暗綠色:

\setuphead[title][align=middle,color=darkgreen]

效果如下圖所示:

以上對文章標題的位置和外觀的調整,展現了 TeX 世界的能力所及之萬一,儘管如此,倘若我提出一個並不多麼刁鑽的問題,如何讓標題裡的字母序列從綠色漸變為紅色?就會讓 TeX 世界感到乏力。若有不信,可嘗試使用 TeX 語言解決以下兩個問題:

  • 遍歷一段文字,訪問它的每個字元。
  • 小數的加法運算,例如計算 0.1 + 0.3。

這兩個問題,實質上正是解決我上面提出的讓標題字元顏色漸變問題的關鍵。第一個問題,閱讀《The TeX book》到第 20 章的時候,倘若依然氣定神閒,興許舉手之勞便可解決。至於第二個問題,倘若不動用一些 TeX 駭客們煞費苦心編寫的宏包 1,那就要自己煞費苦心去寫這樣的宏包——需要大量的控制序列的組合,方能解決。

Lua 世界如何解決文字的遍歷問題呢?在 Lua 語言裡,一段文字可以表示為一個字串,例如

local title = "Hello world!"

要遍歷 title 的每個字元並賦之以漸變的顏色,只需

local red, green, step  = 0, 1, (1 / #title)

function fancy_char(c)
    red = red + step
    if red > 1 then red = 1 end
    green = green - step
    if green < 0 then green = 0 end
    context("\\definecolor[fancy][r=%f,g=%f,b=0]", red, green)
    context("\\color[fancy]{" .. c .. "}")
end

string.gsub(title, ".", fancy_char)

上述 Lua 程式碼可產生經 Lua 直譯器執行後,可產生一組 TeX 控制序列,它們與以下控制序列等價:

\definecolor[fancy][r=0.083333333333333,g=0.91666666666667,b=0]%
\color[fancy]{H}%
\definecolor[fancy][r=0.16666666666667,g=0.83333333333333,b=0]%
\color[fancy]{e}%
\definecolor[fancy][r=0.25,g=0.75,b=0]%
\color[fancy]{l}%
\definecolor[fancy][r=0.33333333333333,g=0.66666666666667,b=0]%
\color[fancy]{l}%
\definecolor[fancy][r=0.41666666666667,g=0.58333333333333,b=0]%
\color[fancy]{o}%
\definecolor[fancy][r=0.5,g=0.5,b=0]%
\color[fancy]{ }%
\definecolor[fancy][r=0.58333333333333,g=0.41666666666667,b=0]%
\color[fancy]{w}%
\definecolor[fancy][r=0.66666666666667,g=0.33333333333333,b=0]%
\color[fancy]{o}%
\definecolor[fancy][r=0.75,g=0.25,b=0]%
\color[fancy]{r}%
\definecolor[fancy][r=0.83333333333333,g=0.16666666666667,b=0]%
\color[fancy]{l}%
\definecolor[fancy][r=0.91666666666667,g=0.083333333333333,b=0]%
\color[fancy]{d}%
\definecolor[fancy][r=1.0,g=0,b=0]%
\color[fancy]{!}%

這組 TeX 控制序列可在 PDF 檔案裡輸出顏色漸變的文字:

倘若將上述的 Lua 程式碼嵌入到 ConTeXt 原始檔裡,便可實現文章標題顏色的漸變了。例如

\startluacode
local title, step
local red, green = 0, 1

function fancy_char(c)
    red = red + step
    if red > 1 then red = 1 end
    green = green - step
    if green < 0 then green = 0 end
    context("\\definecolor[fancy][r=%f,g=%f,b=0]", red, green)
    context("\\color[fancy]{" .. c .. "}")
end

function fancy(text)
    title = text
    step = 1 / #title
    string.gsub(title, ".", fancy_char)
end
\stopluacode

\define[1]\fancy{\ctxlua{fancy([=[#1]=])}}
\setuphead[title][align=middle,deeptextcommand=\fancy]

\starttext
\title{Hello world!}

... some text ...

\stoptext

生成的 PDF 如下圖所示:

倘若此刻看不懂上述程式碼,勿須焦慮。就像每天出門,我們看到的這個世界,誰能真正熟悉它呢?上述程式碼,展現的是一個複雜的世界的概貌速寫……不對,是兩個世界的……而造訪它們的旅程,從此刻剛剛開始。

下一篇:卡片

相關文章