|
| 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