上一篇:偽豎排
對每個人最公平的莫過於時間。絕大多數人的時間,被極少數的人以花樣繁多的管理學手段悄悄偷去了一部分。竊錢者蹲監獄,竊時間者為老闆。若每個人都能管好自己的時間,天下必將大同。
時間戳增強版
回顧一下之前在 card-env.tex 檔案裡定義的時間戳:
\startluacode
my = {}
function my.is_cjk_char(c)
if c >= 0x3400 and c <= 0x4db5
or c >= 0x4e00 and c <= 0x9fa5
or c >= 0x9fa6 and c <= 0x9fbb
or c >= 0xf900 and c <= 0xfa2d
or c >= 0xfa30 and c <= 0xfa6a
or c >= 0xfa70 and c <= 0xfad9
or c >= 0x20000 and c <= 0x2a6d6
or c >= 0x2f800 and c <= 0x2fa1d
or c >= 0xff00 and c <= 0xffef
or c >= 0x2e80 and c <= 0x2eff
or c >= 0x3000 and c <= 0x303f
or c >= 0x31c0 and c <= 0x31ef then
return true;
else
return false;
end
end
function my.rotate(x, a)
pad = "\\kern.125em"
for _, c in utf8.codes(x) do
if my.is_cjk_char(c) then
context("%s{\\rotate[rotation=%d]{%s}}%s", pad, a, utf8.char(c), pad)
else
context("{\\raise.5\\maxdepth\\hbox{%s}}", utf8.char(c))
end
end
end
\stopluacode
\def\timestamp#1{\rotate[rotation=270]{\ctxlua{my.rotate("#1", 90)}}}
\setuptexttexts
[margin]
[][\hfill{\timestamp{2023 年 01 月 26 日 凌晨 04 時 44 分}}\hfill]
現在,將 \timestamp
定義為
\def\timestamp#1{%
\page
\setuptexttexts
[margin][][\hfill{\rotate[rotation=270]{\ctxlua{my.rotate("#1", 90)}}}\hfill]
}
重定義的 \timestamp
宏接受一個引數,使用 \page
另起新頁,將引數內容安放於頁面右側留白區域。下面是 \timestamp
的用法示例:
\environment card-env
\starttext
\timestamp{2023 年 01 月 30 日}
測試頁 1
\timestamp{2023 年 01 月 31 日}
測試頁 2
\stoptext
\page
宏用於分頁,僅在前後皆有內容時有效,例如
\starttext
\page
foo
\page
bar
\page
\stoptext
其中,第一個和第三個 \page
是無用的,會被 TeX 編譯器忽略,只有第二個 \page
有效,使得 foo
和 bar
各佔一頁。
待辦事項
用 ConTeXt 的 xtable 可以模擬風靡一時的 To do List。
首先,構造一個三列的表格:
\environment card-env
\starttext
\timestamp{2023 年 01 月 31 日}
\definextable[todolist]
\startxtable[todolist]
\startxrow
\startxcell $\circ$ \stopxcell
\startxcell 曬太陽 \stopxcell
\startxcell $\checkmark$ \stopxcell
\stopxrow
\startxrow
\startxcell $\circ$ \stopxcell
\startxcell 包餃子 \stopxcell
\startxcell $\checkmark$ \stopxcell
\stopxrow
\startxrow
\startxcell $\circ$ \stopxcell
\startxcell 拖地板 \stopxcell
\startxcell $\checkmark$ \stopxcell
\stopxrow
\stopxtable
\stoptext
倘若排版結果未出現 $\cdot$ 和 $\checkmark$ 符號,可在 card-env.tex 裡設定數學字型:
\definefontfamily[myfont][serif][sourcehanserifcn]
\definefontfamily[myfont][math][xitsmath] % <--- 新增這一行!
\setscript[hanzi]
\setupbodyfont[myfont,7pt]
現在隱藏表格的邊框,將第一列寬度調整為 1.5em
,第二列寬度設定為 .9\textwidth
,即版心寬度的 0.9 倍,第三列寬度設定為 .1\textwidth
:
\setupxtable[todolist][frame=off]
\startxtable[todolist]
\startxrow
\startxcell[width=1.5em] $\circ$ \stopxcell
\startxcell[width=.9\textwidth] 曬太陽 \stopxcell
\startxcell[width=.1\textwidth] $\checkmark$ \stopxcell
\stopxrow
\startxrow
\startxcell $\circ$ \stopxcell
\startxcell 包餃子 \stopxcell
\startxcell $\checkmark$ \stopxcell
\stopxrow
\startxrow
\startxcell $\circ$ \stopxcell
\startxcell 拖地板 \stopxcell
\startxcell \stopxcell
\stopxrow
\stopxtable
Lua 表 -> ConTeXt 表格
為了增加一條待辦事項,需要寫一堆命令,不勝其繁。由於這些宏的出現具有重複性,因此可以考慮用 Lua 程式設計的方式予以簡化,因為在 Lua 語言裡,表格的語法非常簡單。例如
\startxtable
\startxrow
\startxcell 1\stopxcell
\startxcell 2\stopxcell
\startxcell 3\stopxcell
\stopxrow
\stopxtable
所表達的表格形式,用 Lua 的表結構可表述為
{1, 2, 3}
下面的示例可將 Lua 表結構轉化為 ConTeXt 表格:
\environment card-env
\startluacode
my = my or {}
local ctx = context
function my.add(row)
ctx.startxrow()
for _, v in ipairs(row) do
ctx.startxcell(); context(v); ctx.stopxcell()
end
ctx.stopxrow()
end
\stopluacode
\def\foo#1{\ctxlua{my.add({#1})}}
\starttext
% 將表格單元長寬均設為 2em,且令內容居中
\setupxtable[width=2em, height=2em, align={middle,lohi}]
\startxtable
\foo{4, 9, 2}
\foo{3, 5, 7}
\foo{8, 1, 6}
\stopxtable
\stoptext
以上述 Lua 程式碼為基礎,略加修改,便可簡化待辦事項的新增:
\environment card-env
\definextable[todolist]
\setupxtable[todolist][frame=off]
\startluacode
my = my or {}
local ctx = context
local dim = number.todimen
local textwidth = tex.dimen.textwidth
function my.task(task, status)
ctx.startxrow()
-- 第一列
ctx.startxcell{width=dim(tex.sp("1.5em"))};
context("$\\circ$");
ctx.stopxcell()
-- 第二列
ctx.startxcell{width=dim(0.9 * textwidth)};
context("%s", task);
ctx.stopxcell()
-- 第三列
ctx.startxcell{width=dim(0.1 * textwidth),align="{middle,lohi}"};
context(status);
ctx.stopxcell()
ctx.stopxrow()
end
\stopluacode
\def\task#1#2{\ctxlua{my.task("#1", "#2")}}
\starttext
\timestamp{2023 年 01 月 31 日}
\startxtable[todolist]
\task{曬太陽}{$\\checkmark$}
\task{包餃子}{$\\checkmark$}
\task{拖地板}{}
\stopxtable
\stoptext
注意,在上述程式碼裡,\circ
和 \checkmark
裡的反斜線符號 \
出現在 Lua 語境裡時,需要新增反斜線 \
予以轉義。若要規避該問題,可以使用 Lua 的長字串語法。
Lua 長字串
以下 Lua i程式碼定義了 4 個字串變數:
local a = "\\starttext ... \\stoptext"
local b = [[\starttext ... \stoptext]]
local c = [=[\starttext ... \stoptext]=]
local d = [==[\starttext ... \stoptext]==]
其中,a
的值用的是 Lua 短字串語法,而 b
,c
和 d
的值用的是 Lua 長字串語法。在長字串語法中,\
符號無需轉義,且字串可以直接換行,無需使用換行符 \n
。例如,短字串
local a = "\\starttext\n ... \n\\stoptext"
與之等價的長字串為
local a = [[\starttext
...
\stoptext]]
基於 Lua 長字串語法,待辦事項的宏定義與用法示例可修改為
\environment card-env
\definextable[todolist]
\setupxtable[todolist][frame=off]
\startluacode
my = my or {}
local ctx = context
local dim = number.todimen
local textwidth = tex.dimen.textwidth
function my.task(task, status)
ctx.startxrow()
-- 第一列
ctx.startxcell{width=dim(tex.sp("1.5em"))};
context([[$\circ$]]);
ctx.stopxcell()
-- 第二列
ctx.startxcell{width=dim(0.9 * textwidth)};
context([[%s]], task);
ctx.stopxcell()
-- 第三列
ctx.startxcell{width=dim(0.1 * textwidth),align="{middle,lohi}"};
context(status);
ctx.stopxcell()
ctx.stopxrow()
end
\stopluacode
\def\task#1#2{\ctxlua{my.task([[#1]], [[#2]])}}
\starttext
\timestamp{2023 年 01 月 31 日}
\startxtable[todolist]
\task{曬太陽}{$\checkmark$}
\task{包餃子}{$\checkmark$}
\task{拖地板}{}
\stopxtable
\stoptext
將上述程式碼中的 \environment
和 \starttext
語句之間的程式碼挪移到 card-env.tex 檔案。
結語
card.tex:
\environment card-env
\starttext
\timestamp{2023 年 01 月 31 日}
\startxtable[todolist]
\task{曬太陽}{$\checkmark$}
\task{包餃子}{$\checkmark$}
\task{拖地板}{}
\stopxtable
\stoptext
card-env.tex:
% 頁面佈局
\definepapersize[card][width=85.6mm,height=53.98mm]
\setuppapersize[card]
\setuplayout
[backspace=.1\paperwidth,
width=.8\paperwidth,
topspace=.015\paperheight,
height=.97\paperheight,
leftmargin=.666\backspace,
rightmargin=.666\cutspace,
headerdistance=.025\makeupheight,
footerdistance=.025\makeupheight,
textheight=.95\makeupheight]
% 字型
\definefontfamily[myfont][serif][sourcehanserifcn]
\definefontfamily[myfont][math][xitsmath]
\setscript[hanzi]
\setupbodyfont[myfont,7pt]
% 頁碼
\setuppagenumbering[location=]
\setupfootertexts[margin][][\hfill\pagenumber\hfill]
% 標題
\setuphead[title][align=middle]
% 時間戳
\startluacode
my = {}
function my.is_cjk_char(c)
if c >= 0x3400 and c <= 0x4db5
or c >= 0x4e00 and c <= 0x9fa5
or c >= 0x9fa6 and c <= 0x9fbb
or c >= 0xf900 and c <= 0xfa2d
or c >= 0xfa30 and c <= 0xfa6a
or c >= 0xfa70 and c <= 0xfad9
or c >= 0x20000 and c <= 0x2a6d6
or c >= 0x2f800 and c <= 0x2fa1d
or c >= 0xff00 and c <= 0xffef
or c >= 0x2e80 and c <= 0x2eff
or c >= 0x3000 and c <= 0x303f
or c >= 0x31c0 and c <= 0x31ef then
return true;
else
return false;
end
end
function my.rotate(x, a)
pad = "\\kern.125em"
for _, c in utf8.codes(x) do
if my.is_cjk_char(c) then
context("%s{\\rotate[rotation=%d]{%s}}%s", pad, a, utf8.char(c), pad)
else
context("{\\raise.5\\maxdepth\\hbox{%s}}", utf8.char(c))
end
end
end
\stopluacode
\def\timestamp#1{%
\page
\setuptexttexts
[margin]
[][\hfill{\rotate[rotation=270]{\ctxlua{my.rotate("#1", 90)}}}\hfill]
}
% 待辦事項
\definextable[todolist]
\setupxtable[todolist][frame=off]
\startluacode
my = my or {}
local ctx = context
local dim = number.todimen
local textwidth = tex.dimen.textwidth
function my.task(task, status)
ctx.startxrow()
-- 第一列
ctx.startxcell{width=dim(tex.sp("1.5em"))};
context([[$\circ$]]);
ctx.stopxcell()
-- 第二列
ctx.startxcell{width=dim(0.9 * textwidth)};
context([[%s]], task);
ctx.stopxcell()
-- 第三列
ctx.startxcell{width=dim(0.1 * textwidth),align="{middle,lohi}"};
context(status);
ctx.stopxcell()
ctx.stopxrow()
end
\stopluacode
\def\task#1#2{\ctxlua{my.task([[#1]], [[#2]])}}
下一篇:引數列表解析
參考
- xtable
- luametatex, p254.
- ConTeXt Lua Documents, p41, p189.