if not modules then modules = { } end modules ['cloze'] = { version = '0.1', comment = 'cloze', author = 'Josef Friedrich, R.-M. Huber', copyright = 'Josef Friedrich, R.-M. Huber', license = 'The LaTeX Project Public License Version 1.3c 2008-05-04' } local nodex = {} local registry = {} registry.user_id = 3121978 registry.storage = {} registry.defaults = { ['align'] = 'l', ['distance'] = '1.5pt', ['hide'] = false, ['linecolor'] = '0 0 0 rg 0 0 0 RG', -- black ['margin'] = '3pt', ['resetcolor'] = '0 0 0 rg 0 0 0 RG', -- black ['show_text'] = true, ['show'] = true, ['textcolor'] = '0 0 1 rg 0 0 1 RG', -- blue ['thickness'] = '0.4pt', ['width'] = '2cm', } registry.global_options = {} registry.local_options = {} local cloze = {} local base = {} base.is_registered = {} function nodex.create_colorstack(data) if not data then data = '0 0 0 rg 0 0 0 RG' -- black end local whatsit = node.new('whatsit', 'pdf_colorstack') whatsit.stack = 0 whatsit.data = data return whatsit end function nodex.create_color(option) local data if option == 'line' then data = registry.get_value('linecolor') elseif option == 'text' then data = registry.get_value('textcolor') elseif option == 'reset' then data = nil else data = nil end return nodex.create_colorstack(data) end function nodex.create_line(width) local rule = node.new(node.id('rule')) local thickness = tex.sp(registry.get_value('thickness')) local distance = tex.sp(registry.get_value('distance')) rule.depth = distance + thickness rule.height = - distance rule.width = width return rule end function nodex.insert_line(head, current, width) local n = {} -- node n.color_line = nodex.create_color('line') head, n.color_line = node.insert_after(head, current, n.color_line) n.line = nodex.create_line(width) head, n.line = node.insert_after(head, n.color_line, n.line) n.color_reset = nodex.create_color('reset') return node.insert_after(head, n.line, n.color_reset) end function nodex.write_line() node.write(nodex.create_color('line')) node.write(nodex.create_line(tex.sp(registry.get_value('width')))) node.write(nodex.create_color('reset')) end function nodex.create_linefil() local glue = node.new('glue') glue.subtype = 100 glue.stretch = 65536 glue.stretch_order = 3 local rule = nodex.create_line(0) rule.dir = 'TLT' glue.leader = rule return glue end function nodex.write_linefil() node.write(nodex.create_color('line')) node.write(nodex.create_linefil()) node.write(nodex.create_color('reset')) end function nodex.create_kern(width) local kern = node.new(node.id('kern')) kern.kern = width return kern end function nodex.strut_to_hlist(hlist) local n = {} -- node n.head = hlist.head n.kern = nodex.create_kern(0) n.strut = node.insert_before(n.head, n.head, n.kern) hlist.head = n.head.prev return hlist, n.strut, n.head end function nodex.write_margin() local kern = nodex.create_kern(tex.sp(registry.get_value('margin'))) node.write(kern) end function registry.create_marker(index) local marker = node.new('whatsit','user_defined') marker.type = 100 -- number marker.user_id = registry.user_id marker.value = index return marker end function registry.write_marker(mode, position) local index = registry.set_storage(mode, position) local marker = registry.create_marker(index) node.write(marker) end function registry.check_marker(item, mode, position) local data = registry.get_marker_data(item) if data and data.mode == mode and data.position == position then return true else return false end end function registry.get_marker(item, mode, position) local out if registry.check_marker(item, mode, position) then out = item else out = false end if out and position == 'start' then registry.get_marker_values(item) end return out end function registry.get_marker_data(item) if item.id == node.id('whatsit') and item.subtype == node.subtype('user_defined') and item.user_id == registry.user_id then return registry.get_storage(item.value) else return false end end function registry.get_marker_values(marker) local data = registry.get_marker_data(marker) registry.local_options = data.values return data.values end function registry.get_index() if not registry.index then registry.index = 0 end registry.index = registry.index + 1 return registry.index end function registry.set_storage(mode, position) local index = registry.get_index() local data = { ['mode'] = mode, ['position'] = position } data.values = registry.local_options registry.storage[index] = data return index end function registry.get_storage(index) return registry.storage[index] end function registry.set_option(key, value) if value == '' or value == '\\color@ ' then return false end if registry.is_global == true then registry.global_options[key] = value else registry.local_options[key] = value end end function registry.set_is_global(value) registry.is_global = value end function registry.unset_local_options() registry.local_options = {} end function registry.unset_global_options() registry.global_options = {} end function registry.get_value(key) if registry.has_value(registry.local_options[key]) then return registry.local_options[key] end if registry.has_value(registry.global_options[key]) then return registry.global_options[key] end return registry.defaults[key] end function registry.get_value_show() if registry.get_value('show') == true or registry.get_value('show') == 'true' then return true else return false end end function registry.has_value(value) if value == nil or value == '' or value == '\\color@ ' then return false else return true end end function registry.get_defaults(option) return registry.defaults[option] end function cloze.basic_make(head, hlist, start, stop) local n = {} local l = {} if not start or not stop then return end n.start = start n.stop = stop l.width = node.dimensions( hlist.glue_set, hlist.glue_sign, hlist.glue_order, n.start, n.stop ) head, n.line = nodex.insert_line(head, n.start, l.width) n.color_text = nodex.create_color('text') head, n.color_text = node.insert_after( hlist.head, n.line, n.color_text ) if registry.get_value_show() then n.kern = nodex.create_kern(-l.width) node.insert_after(head, n.color_text, n.kern) n.color_reset = nodex.create_color('reset') node.insert_after(head, n.stop, n.color_reset) else n.line.next = n.stop.next n.stop.prev = n.line.prev end end function cloze.basic(head) local n = {} -- node local b = {} -- boolean local l = {} -- length local t = {} -- temp for hlist in node.traverse_id(node.id('hlist'), head) do hlist = nodex.strut_to_hlist(hlist) if b.line_end then b.init_cloze = true end n.current = hlist.head while n.current do if registry.check_marker(n.current, 'basic', 'start') or b.init_cloze then b.init_cloze = false n.start = n.current while n.current do b.line_end = true n.stop = n.current if registry.check_marker(n.stop, 'basic', 'stop') then b.line_end = false break end n.current = n.current.next end cloze.basic_make(head, hlist, n.start, n.stop) n.current = n.stop else n.current = n.current.next end end end return head end function cloze.fix_make(head, start, stop) local l = {} -- length l.width = tex.sp(registry.get_value('width')) local n = {} -- node n.start = start n.stop = stop l.text_width = node.dimensions(n.start, n.stop) local align = registry.get_value('align') if align == 'right' then l.kern_start = -l.text_width l.kern_stop = 0 elseif align == 'center' then l.half = (l.width - l.text_width) / 2 l.kern_start = -l.half - l.text_width l.kern_stop = l.half else l.kern_start = -l.width l.kern_stop = l.width - l.text_width end head, n.line = nodex.insert_line(head, n.start, l.width) if registry.get_value_show() then n.kern_start = nodex.create_kern(l.kern_start) head, n.kern_start = node.insert_after(head, n.line, n.kern_start) n.color_text = nodex.create_color('text') node.insert_after(head, n.kern_start, n.color_text) n.color_reset = nodex.create_color('reset') node.insert_before(head, n.stop, n.color_reset) n.kern_stop = nodex.create_kern(l.kern_stop) node.insert_before(head, n.stop, n.kern_stop) else n.line.next = n.stop.next end end function cloze.fix(head) local n = {} -- node n.start, n.stop = false for current in node.traverse_id(node.id('whatsit'), head) do if not n.start then n.start = registry.get_marker(current, 'fix', 'start') end if not n.stop then n.stop = registry.get_marker(current, 'fix', 'stop') end if n.start and n.stop then cloze.fix_make(head, n.start, n.stop) n.start, n.stop = false end end return head end function cloze.par(head) local l = {} -- length local n = {} -- node for hlist in node.traverse_id(node.id('hlist'), head) do for whatsit in node.traverse_id(node.id('whatsit'), hlist.head) do registry.get_marker(whatsit, 'par', 'start') end l.width = hlist.width hlist, n.strut, n.head = nodex.strut_to_hlist(hlist) head, n.line = nodex.insert_line(head, n.strut, l.width) if registry.get_value_show() then n.kern = nodex.create_kern(-l.width) head, n.kern = node.insert_after(head, n.line, n.kern) n.color_text = nodex.create_color('text') node.insert_after(head, n.kern, n.color_text) n.tail = node.tail(n.head) n.color_reset = nodex.create_color('reset') node.insert_after(n.head, n.tail, n.color_reset) else n.line.next = nil end end return head end function base.register(mode) if mode == 'par' then luatexbase.add_to_callback( 'post_linebreak_filter', cloze.par, mode ) return true end if not base.is_registered[mode] then if mode == 'basic' then luatexbase.add_to_callback( 'post_linebreak_filter', cloze.basic, mode ) elseif mode == 'fix' then luatexbase.add_to_callback( 'pre_linebreak_filter', cloze.fix, mode ) else return false end base.is_registered[mode] = true end end function base.unregister(mode) if mode == 'basic' then luatexbase.remove_from_callback('post_linebreak_filter', mode) elseif mode == 'fix' then luatexbase.remove_from_callback('pre_linebreak_filter', mode) else luatexbase.remove_from_callback('post_linebreak_filter', mode) end end base.linefil = nodex.write_linefil base.line = nodex.write_line base.margin = nodex.write_margin base.set_option = registry.set_option base.set_is_global = registry.set_is_global base.unset_local_options = registry.unset_local_options base.reset = registry.unset_global_options base.get_defaults = registry.get_defaults base.marker = registry.write_marker return base