Skip to content

Commit cea7c30

Browse files
committed
review
1 parent f8cb43a commit cea7c30

7 files changed

+2038
-2
lines changed

README.md

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
适用于ConTeXt和LuaMetaTeX(LMTX, LuaTeX的后继者,ConTeXt当前的实际引擎)的夹注模块,可以配合[竖排功能和标点压缩功能](https://blog.xiiigame.com/2022-02-15-ConTeXt-LMTX%E4%B8%AD%E6%96%87%E7%AB%96%E6%8E%92%E6%8F%92%E4%BB%B6/)使用。调整后应该可用于LuaTeX。
22

3-
也是本人学习LuaMetaTeX、LuaTeX的练习项目,主要涉及:段落与行的结点列表的操控;使用tex.linebreak()函数等干预断行;使用回调函数。会保留较多学习性的注释和代码。
4-
53
## 安装和使用方法
64

75
* 两种安装方法:

TEMP/jiazhu-bak.lua

+341
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
Moduledata = Moduledata or {}
2+
Moduledata.jiazhu = Moduledata.jiazhu or {}
3+
4+
-- 本地化以提高运行效率
5+
6+
local glue_id = nodes.nodecodes.glue --node.id("glue")
7+
local glyph_id = nodes.nodecodes.glyph
8+
local hlist_id = nodes.nodecodes.hlist
9+
local par_id = nodes.nodecodes.par
10+
local penalty_id = nodes.nodecodes.penalty
11+
local rule_id = nodes.nodecodes.rule
12+
local vlist_id = nodes.nodecodes.vlist
13+
14+
local node_copylist = node.copylist
15+
local node_count = node.count
16+
local node_dimensions = node.dimensions
17+
local node_flushlist = node.flushlist
18+
local node_free = node.free
19+
local node_hasattribute = node.hasattribute
20+
local node_hpack = node.hpack
21+
local node_insertafter = node.insertafter
22+
local node_insertbefore = node.insertbefore
23+
local node_kerning = node.kerning
24+
local node_ligaturing = node.ligaturing
25+
local node_new = node.new
26+
local node_remove = node.remove
27+
local node_setattribute = node.setattribute
28+
local node_slide = node.slide
29+
local node_traverse = node.traverse
30+
local node_traverseid = node.traverseid
31+
local node_vpack = node.vpack
32+
33+
local tex_dimen_textwidth = tex.dimen.textwidth
34+
local tex_linebreak = tex.linebreak
35+
local tex_sp = tex.sp
36+
37+
---[[ 结点跟踪工具
38+
local function show_detail(n, label)
39+
print(">>>>>>>>>"..label.."<<<<<<<<<<")
40+
print(nodes.toutf(n))
41+
for i in node.traverse(n) do
42+
local char
43+
if i.id == glyph_id then
44+
char = utf8.char(i.char)
45+
print(i, char)
46+
elseif i.id == penalty_id then
47+
print(i, i.penalty)
48+
elseif i.id == glue_id then
49+
print(i, i.width, i.stretch,i.stretchorder, i.shrink, i.shrinkorder)
50+
elseif i.id == hlist_id then
51+
print(i, nodes.toutf(i.list))
52+
else
53+
print(i)
54+
end
55+
end
56+
end
57+
--]]
58+
59+
-- 用rule代替夹注盒子,并收集夹注盒子
60+
local function boxes_to_rules(head)
61+
local n = head
62+
local jiazhu_boxes = {}
63+
local done = false
64+
local out_head = nil
65+
while n do
66+
if node_hasattribute(n, 2, 222) and n.id == hlist_id then
67+
local w = node_new(rule_id)
68+
w.width = tex_sp("1em")
69+
node_setattribute(w, 3, 333)
70+
head = node_insertbefore(head, n, w)
71+
local removed
72+
head, n, removed = node_remove(head, n)
73+
table.insert(jiazhu_boxes, removed)
74+
done = true
75+
end
76+
n = n.next
77+
end
78+
if done then
79+
out_head = head
80+
end
81+
node_flushlist(n)
82+
return out_head, jiazhu_boxes
83+
end
84+
85+
-- 试排主段落 hsize:宽度;to_stretch:尾部拉伸(否则压缩)
86+
local function par_break(par_head, hsize, to_stretch)
87+
88+
local new_head = node_copylist(par_head)
89+
90+
local is_vmode_par = (new_head.id == par_id)
91+
92+
if not is_vmode_par then
93+
local vmodepar_node = node_new("par", "vmodepar")
94+
new_head, _ = node_insertbefore(new_head, new_head, vmodepar_node)
95+
end
96+
97+
-- 删除最后的胶;添加无限罚分和parfillskip;保障pre指针正确
98+
local tail = node_slide(new_head) --node.tail()不检查前后指针
99+
if tail.id == glue_id then
100+
new_head, tail = node_remove(new_head, tail, true)
101+
node_free(tail)
102+
end
103+
104+
-- 添加无限罚分
105+
local penalty = node_new("penalty")
106+
penalty.penalty = 10000
107+
new_head, tail = node_insertafter(new_head, tail, penalty)
108+
109+
-- 添加hskip
110+
local hskip = node_new("glue")
111+
if to_stretch then
112+
hskip.width = 0
113+
hskip.stretch = hsize
114+
else
115+
hskip.width = hsize -- 能模仿系统断行
116+
hskip.shrink = hsize -- 能模仿系统断行
117+
end
118+
new_head, tail = node_insertafter(new_head, tail, hskip)
119+
120+
-- 添加段末胶parfillskip( TODO 似乎不起作用)
121+
local parfillskip = node_new("glue", "parfillskip") --一般是0pt plus 1fil
122+
parfillskip.stretch = 2^16 -- 可拉伸量2^16(65536)意义不明
123+
parfillskip.stretchorder = 2^16 --拉伸倍数2(fil),意义不明
124+
new_head, tail = node_insertafter(new_head, tail, parfillskip)
125+
126+
-- 保障prev指针正确
127+
node_slide(new_head)
128+
129+
language.hyphenate(new_head) -- 断词,给单词加可能的连字符断点
130+
new_head = node_kerning(new_head) -- 加字间(出格)
131+
new_head = node_ligaturing(new_head) -- 西文合字
132+
133+
-- 用hsize控制分行宽度:{hsize = tex_sp("25em")};
134+
-- 语言设置作用不详:{lang = tex.language}
135+
-- , lefskip = leftskip_spec 不起作用
136+
-- local leftskip_spec = node_new("gluespec")
137+
-- leftskip_spec.width = tex_sp("5em")
138+
-- leftskip_spec.stretch = tex_sp("5em")
139+
-- leftskip_spec.stretchorder = 2
140+
local para = {hsize=hsize}
141+
local info
142+
new_head, info = tex_linebreak(new_head, para)
143+
144+
return new_head, info
145+
end
146+
147+
-- 测量夹注宽度
148+
local function jiazhu_hsize(hlist, current_n)
149+
-- 后面的实际宽度(包括突出)、高度、深度
150+
local d = node_dimensions(
151+
hlist.glueset,
152+
hlist.gluesign,
153+
hlist.glueorder,
154+
current_n
155+
)
156+
return d
157+
end
158+
159+
-- 生成双行夹注
160+
local function make_jiazhu_box(hsize, boxes)
161+
local b = boxes[1]
162+
local box_width = jiazhu_hsize(b, b.head) -- 实际测量宽度,不适用width属性
163+
-- show_detail(b.head,"here")
164+
local b_list = b.list
165+
local to_remove -- 本条已经完成,需要移除
166+
local to_break_after = false -- 本条在行末,需要断行
167+
168+
-- 夹注重排算法
169+
local width_tolerance = tex_sp("0.4em") -- 宽容宽度(挤进一行)
170+
local max_hsize = hsize + width_tolerance
171+
local min_hsize = hsize - width_tolerance
172+
local step = width_tolerance / 4 --步进控制 TODO 优化
173+
local vbox_width = box_width / 2
174+
local box_head, info
175+
-- 可一次(两行)安排完的短盒子
176+
if vbox_width <= max_hsize then
177+
local line_num = 3
178+
vbox_width = vbox_width - 2 * step --步进控制 TODO 优化
179+
while(line_num >= 3) do
180+
box_head, info = par_break(b_list, vbox_width, true)
181+
line_num = info.prevgraf
182+
vbox_width = vbox_width + step -- TODO 改进步进量或段末胶
183+
end
184+
-- 其后强制断行
185+
local actual_vbox_width = vbox_width - step
186+
if actual_vbox_width >= min_hsize and actual_vbox_width <= max_hsize then
187+
to_break_after = true
188+
end
189+
-- 清除rule标记
190+
to_remove = true
191+
node_flushlist(boxes[1].head)
192+
table.remove(boxes, 1)
193+
else -- 需要循环安排的长盒子
194+
box_head, info = par_break(b_list, hsize, false)
195+
196+
-- 只取前两行所包含的节点
197+
local line_num = 0
198+
local glyph_num = 0
199+
for i in node_traverseid(hlist_id, box_head) do
200+
line_num = line_num + 1
201+
-- 计算字模、列表数量 TODO 计数优化,还应该增加类型,如rule等
202+
glyph_num = glyph_num + node_count(glyph_id, i.head)
203+
glyph_num = glyph_num + node_count(hlist_id, i.head)
204+
if line_num == 2 then
205+
box_head = node_copylist(box_head, i.next)
206+
break --计数法
207+
end
208+
end
209+
210+
-- 截取未用的盒子列表 TODO 相应优化
211+
for i in node_traverse(b_list) do
212+
if i.id == glyph_id or i.id == hlist_id then
213+
glyph_num = glyph_num - 1
214+
if glyph_num == -1 then
215+
local hlist = node_hpack(node_copylist(i))
216+
node_flushlist(boxes[1].head)
217+
boxes[1] = hlist
218+
end
219+
end
220+
end
221+
222+
to_break_after = true
223+
to_remove = false
224+
end
225+
226+
-- 打包,修改包的高度和行距
227+
box_head = node_vpack(box_head)
228+
local skip = tex_sp("0.08em") -- 夹注行间距
229+
local sub_glue_h = 0 -- 计算删除的胶高度
230+
local n = box_head.head
231+
local count = 0
232+
while n do
233+
if n.id == glue_id then
234+
count = count + 1
235+
if count == 1 then
236+
sub_glue_h = sub_glue_h + n.width
237+
-- 删除第一个胶
238+
box_head.head, n = node_remove(box_head.head, n, true)
239+
else
240+
-- 更改中间的胶
241+
sub_glue_h = sub_glue_h + (n.width - skip)
242+
n.width = skip
243+
n = n.next
244+
end
245+
else
246+
n = n.next
247+
end
248+
end
249+
250+
local box_head_height = box_head.height - sub_glue_h
251+
local baseline_to_center = tex_sp("0.4em") -- TODO 应根据字体数据计算
252+
box_head.height = baseline_to_center + box_head_height/ 2
253+
box_head.depth = box_head_height - box_head.height
254+
255+
return box_head, boxes, to_remove, to_break_after
256+
end
257+
258+
-- 根据第一个rule的位置分拆、组合、插入夹注盒子、罚点等
259+
local function insert_jiazhu(head_with_rules, vpar_head, jiazhu_boxes)
260+
-- local stop = false
261+
-- 寻找行,寻找rule
262+
for h,_ in node_traverseid(hlist_id, vpar_head) do
263+
for r, _ in node_traverseid(rule_id,h.head) do
264+
if node_hasattribute(r,3,333) then
265+
local hsize = jiazhu_hsize(h, r) -- 夹注标记rule到行尾的长度
266+
local to_remove, jiazhu_box, to_break_after
267+
jiazhu_box, jiazhu_boxes, to_remove, to_break_after = make_jiazhu_box(hsize, jiazhu_boxes)
268+
for rule, _ in node_traverseid(rule_id, head_with_rules) do
269+
if node_hasattribute(rule,3,333) then
270+
-- 插入夹注
271+
head_with_rules, jiazhu_box = node_insertbefore(head_with_rules, rule, jiazhu_box)
272+
-- 插入罚点(必须断行)
273+
local penalty = node_new("penalty")
274+
if to_break_after then
275+
penalty.penalty = -10000
276+
else
277+
penalty.penalty = 0
278+
end
279+
head_with_rules, penalty = node_insertafter(head_with_rules, jiazhu_box, penalty)
280+
-- 移除标记rule
281+
if to_remove then
282+
head_with_rules, rule = node_remove(head_with_rules,rule,true)
283+
else
284+
-- 或,加胶
285+
local glue = node_new("glue")
286+
glue.width = 0
287+
glue.stretch = tex_sp("0.5em")
288+
head_with_rules, glue = node_insertafter(head_with_rules, penalty, glue)
289+
end
290+
node_flushlist(vpar_head)
291+
return head_with_rules, jiazhu_boxes
292+
end
293+
end
294+
print("jiazhu>> 没有找到插入标记。")
295+
end
296+
end
297+
end
298+
end
299+
300+
-- TODO 递归
301+
local function find_fist_rule(par_head_with_rule, boxes)
302+
local n = par_head_with_rule
303+
while n do
304+
if n.id == rule_id and node_hasattribute(n,3,333) then
305+
local hsize = tex_dimen_textwidth -- tex.dimen.hsize
306+
307+
-- TODO par_break改变了head_with_rules
308+
local vpar_head, _= par_break(par_head_with_rule, hsize, false)
309+
310+
-- context(node_copylist(vpar_head))
311+
par_head_with_rule, boxes = insert_jiazhu(par_head_with_rule, vpar_head, boxes)
312+
313+
return find_fist_rule(par_head_with_rule, boxes)
314+
end
315+
316+
n = n.next
317+
end
318+
node_flushlist(n)
319+
return par_head_with_rule
320+
end
321+
322+
function Moduledata.jiazhu.main(head)
323+
local out_head = head
324+
-- 仅处理段落
325+
-- if head.id == par_id then
326+
local par_head_with_rule, jiazhu_boxes = boxes_to_rules(head)
327+
if par_head_with_rule then
328+
out_head = find_fist_rule(par_head_with_rule, jiazhu_boxes)
329+
end
330+
-- end
331+
return out_head, true
332+
end
333+
334+
function Moduledata.jiazhu.register()
335+
-- 只能使用CLD样式添加任务
336+
-- "processors", "before",只加了par vmodepar和左右parfill skip
337+
-- "processors", "after",还加入了字间的glue userskip、标点前后的penalty userpenalty,可用于断行
338+
nodes.tasks.appendaction("processors", "after", "Moduledata.jiazhu.main")
339+
end
340+
341+
return Moduledata.jiazhu

0 commit comments

Comments
 (0)