OSDN Git Service

Implement \yoko and \tate. (font rotation: not yet)
[luatex-ja/luatexja.git] / src / ltj-direction.lua
1 --
2 -- src/ltj-direction.lua
3 --
4
5 luatexja.load_module('base');      local ltjb = luatexja.base
6 luatexja.load_module('stack');     local ltjs = luatexja.stack
7 luatexja.direction = {}
8
9 local attr_dir = luatexbase.attributes['ltj@dir']
10 local attr_icflag = luatexbase.attributes['ltj@icflag']
11
12 local Dnode = node.direct or node
13 local nullfunc = function (n) return n end
14 local to_node = (Dnode ~= node) and Dnode.tonode or nullfunc
15 local to_direct = (Dnode ~= node) and Dnode.todirect or nullfunc
16 local has_attr = Dnode.has_attribute
17 local set_attr = Dnode.set_attribute
18 local insert_before = Dnode.insert_before
19 local insert_after = Dnode.insert_after
20 local getid = (Dnode ~= node) and Dnode.getid or function(n) return n.id end
21 local getsubtype = (Dnode ~= node) and Dnode.getsubtype or function(n) return n.subtype end
22 local getlist = (Dnode ~= node) and Dnode.getlist or function(n) return n.head end
23 local setfield = (Dnode ~= node) and Dnode.setfield or function(n, i, c) n[i] = c end
24 local getfield = (Dnode ~= node) and Dnode.getfield or function(n, i) return n[i] end
25 local node_new = Dnode.new
26 local node_tail = Dnode.tail
27 local node_free = Dnode.free
28 local node_remove = Dnode.remove
29 local node_next = (Dnode ~= node) and Dnode.getnext or node.next
30 local traverse_id = Dnode.traverse_id
31
32 local id_kern = node.id('kern')
33 local id_hlist = node.id('hlist')
34 local id_vlist = node.id('vlist')
35 local id_whatsit = node.id('whatsit')
36 local sid_save = node.subtype('pdf_save')
37 local sid_restore = node.subtype('pdf_restore')
38 local sid_matrix = node.subtype('pdf_setmatrix')
39 local sid_user = node.subtype('user_defined')
40
41 local PROCESSED    = luatexja.icflag_table.PROCESSED
42 local PACKED       = luatexja.icflag_table.PACKED
43 local DIR = luatexja.stack_table_index.DIR
44 local wh_DIR = luatexja.userid_table.DIR
45 local dir_tate = 3
46 local dir_yoko = 4
47
48 do
49   local node_next = node.next
50   local function set_list_direction(v, name)
51     if node.next(tex.nest[tex.nest.ptr].head) then
52       ltjb.package_error('luatexja',
53                          "Use `\\" .. name .. "' at top of list", 
54                          'Direction change command by LuaTeX-ja is available\n'
55                             .. 'only while current list is null.')
56     else
57        ltjs.set_stack_table(luatexja.stack_table_index.DIR, v, true)
58     end
59   end
60   luatexja.direction.set_list_direction = set_list_direction
61 end
62
63 do 
64    local tex_getcount = tex.getcount
65    local function set_dir_flag(h)
66       local new_dir = ltjs.table_current_stack[DIR]
67       local w = node_new(id_whatsit, sid_user)
68       setfield(w, 'user_id', wh_DIR)
69       setfield(w, 'type', 100)
70       setfield(w, 'value', new_dir)
71       setfield(w, 'next', to_direct(h))
72       return to_node(w)
73    end
74    luatexbase.add_to_callback('hpack_filter', set_dir_flag, 'ltj.set_dir_flag', 10000)
75    luatexbase.add_to_callback('vpack_filter', 
76                               function (h)
77                                  local box_set, cl = 0, tex.currentgrouplevel + 1
78                                  for w in traverse_id(id_whatsit, to_direct(h)) do
79                                     if getfield(w, 'value')==cl then box_set = 1; break end
80                                  end
81                                  ltjs.report_stack_level(tex_getcount('ltj@@stack') + box_set)
82                                  return set_dir_flag(h)
83                               end, 'ltj.set_dir_flag', 1)
84    luatexbase.add_to_callback('post_linebreak_filter', 
85                               function (h)
86                                  local new_dir = ltjs.table_current_stack[DIR]
87                                  for line in traverse_id(id_hlist, to_direct(h)) do
88                                     set_attr(line, attr_dir, new_dir)
89                                  end
90                                  return h
91                               end, 'ltj.set_dir_flag', 100)
92
93 end
94
95 local make_dir_node
96 do
97    local get_h =function (w,h,d) return h end 
98    local get_d =function (w,h,d) return d end 
99    local get_h_d =function (w,h,d) return h+d end 
100    local get_h_neg =function (w,h,d) return -h end 
101    local get_d_neg =function (w,h,d) return -d end 
102    local get_w_half =function (w,h,d) return 0.5*w end 
103    local get_w_neg_half =function (w,h,d) return -0.5*w end 
104    local get_w_neg =function (w,h,d) return -w end
105    local get_w =function (w,h,d) return w end
106    local dir_node_aux = {
107       [dir_yoko] = { -- yoko を tate 中で組む
108          width  = get_h_d,
109          height = get_w_half,
110          depth  = get_w_half,
111          [id_hlist] = {
112             { 'kern', get_h }, 
113             { 'whatsit', sid_save },
114             { 'rotate', '0 1 -1 0' },
115             { 'kern', get_w_neg_half },
116             { 'box' },
117             { 'kern', get_w_neg_half },
118             { 'whatsit', sid_restore },
119          },
120          [id_vlist] = {
121             { 'kern', get_w},
122             { 'whatsit', sid_save },
123             { 'rotate', '0 1 -1 0' },
124             { 'box' },
125             { 'kern', get_h_neg},
126             { 'whatsit', sid_restore },
127          },
128       }, 
129       [dir_tate] = { -- tate を yoko 中で組む
130          width  = get_h_d,
131          height = get_w,
132          depth  = function() return 0 end,
133          [id_hlist] = {
134             { 'kern', get_d }, 
135             { 'whatsit', sid_save },
136             { 'rotate', '0 -1 1 0' },
137             { 'kern', get_w_neg },
138             { 'box' },
139             { 'whatsit', sid_restore },
140          }, 
141          [id_vlist] = {
142             { 'whatsit', sid_save },
143             { 'rotate', '0 -1 1 0' },
144             { 'kern', get_h_neg },
145             { 'kern', get_d_neg },
146             { 'box' },
147             { 'whatsit', sid_restore },
148          }, 
149       },
150    }
151
152    make_dir_node = function (head, b, new_dir)
153       -- head: list head, b: box
154       -- return value: (new head), (next of b), (new b), (is_b_dir_node)
155       -- (new b): b か dir_node に被せられた b
156       local old_dir
157       local bh = getlist(b)
158       if bh and getid(bh)==id_whatsit and getsubtype(bh)==sid_user
159           and getfield(bh, 'user_id')==wh_DIR then
160              old_dir = getfield(bh, 'value')
161              setfield(b, 'head', node_next(bh))
162             set_attr(b, attr_icflag, PROCESSED)
163              node_free(bh)
164              print('FROM WHATSit')
165       else
166          old_dir = has_attr(b, attr_dir)
167          if old_dir==0 then old_dir =4 end
168       end
169       print(old_dir, new_dir)
170       if old_dir==new_dir  then 
171          set_attr(b, attr_icflag, PROCESSED)
172          return head, node_next(b), b, false
173       elseif  -old_dir == new_dir  then 
174          return head, node_next(b), b, true
175       else
176          local nh, nb, ret, flag
177          if old_dir < 0 then 
178             -- b itself is a dir node; just unwrap
179             local bc = node_next(node_next(
180                                     node_next(node_next(bh))))
181             node_remove(bh, bc); 
182             nh, nb =  insert_before(head, b, bc), nil
183             nh, nb = node_remove(head, b)
184             setfield(b, 'next', nil); Dnode.flush_list(b)
185             ret, flag = bc, false
186          else
187             local bid = getid(b)
188             local db = node_new(bid) -- dir node
189             nh, nb =  insert_before(head, b, db), nil
190             nh, nb = node_remove(nh, b)
191             local w = getfield(b, 'width')
192             local h = getfield(b, 'height')
193             local d = getfield(b, 'depth')
194             local info = dir_node_aux[old_dir]
195             set_attr(db, attr_dir, -new_dir)
196             set_attr(b, attr_icflag, PROCESSED)
197             set_attr(db, attr_icflag, PROCESSED)
198             setfield(db, 'dir', getfield(b, 'dir'))
199             setfield(db, 'shift', 0)
200             setfield(db, 'width',  info.width(w,h,d))
201             setfield(db, 'height', info.height(w,h,d))
202             setfield(db, 'depth',  info.depth(w,h,d))
203             local db_head, db_tail  = nil
204             for _,v in ipairs(info[bid]) do
205                local cmd, arg, nn = v[1], v[2]
206                if cmd=='kern' then
207                   nn = node_new(id_kern)
208                   setfield(nn, 'kern', arg(w, h, d))
209                elseif cmd=='whatsit' then
210                   nn = node_new(id_whatsit, arg)
211                elseif cmd=='rotate' then
212                   nn = node_new(id_whatsit, sid_matrix)
213                   setfield(nn, 'data', arg)
214                elseif cmd=='box' then
215                   nn = b; setfield(b, 'next', nil)
216                end
217                if db_head then 
218                   insert_after(db_head, db_tail, nn)
219                   db_tail = nn
220                else
221                   db_head, db_tail = nn, nn
222                end
223             end
224             setfield(db, 'head', db_head)
225             ret, flag = db, true
226          end
227          return nh, nb, ret, flag
228       end
229    end
230    local function process_dir_node(head)
231       local h = to_direct(head)
232       local x, new_dir = h, ltjs.table_current_stack[DIR]
233       while x do
234          local xid = getid(x)
235          if (xid==id_hlist and has_attr(x, attr_icflag)~=PACKED) or xid==id_vlist then
236             h, x = make_dir_node(h, x, new_dir)
237          else
238             x = node_next(x)
239          end
240       end
241       return to_node(h)
242    end
243    luatexja.direction.make_dir_node = make_dir_node
244    luatexbase.add_to_callback('vpack_filter', 
245                               process_dir_node, 'ltj.dir_node', 10001)
246 end