diff options
Diffstat (limited to 'tex/context/base/typo-tal.lua')
-rw-r--r-- | tex/context/base/typo-tal.lua | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/tex/context/base/typo-tal.lua b/tex/context/base/typo-tal.lua new file mode 100644 index 000000000..3df8dd00a --- /dev/null +++ b/tex/context/base/typo-tal.lua @@ -0,0 +1,265 @@ +if not modules then modules = { } end modules ['typo-tal'] = { + version = 1.001, + comment = "companion to typo-tal.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- I'll make it a bit more efficient and provide named instances too. + +local next, type = next, type +local div = math.div +local utfbyte = utf.byte + +local nodecodes = nodes.nodecodes +local glyph_code = nodecodes.glyph +local glue_code = nodecodes.glue + +local fontcharacters = fonts.hashes.characters +local categories = characters.categories -- nd + +local insert_node_before = nodes.insert_before +local insert_node_after = nodes.insert_after +local traverse_list_by_id = nodes.traverse_id +local dimensions_of_list = nodes.dimensions +local first_glyph = nodes.first_glyph + +local nodepool = nodes.pool +local new_kern = nodepool.kern +local new_gluespec = nodepool.gluespec + +local tracers = nodes.tracers +local setcolor = tracers.colors.set +local tracedrule = tracers.pool.nodes.rule + +local characteralign = { } +typesetters.characteralign = characteralign + +local trace_split = false trackers.register("typesetters.characteralign", function(v) trace_split = true end) +local report = logs.reporter("aligning") + +local a_characteralign = attributes.private("characteralign") +local a_character = attributes.private("characters") + +local enabled = false + +local datasets = false + +local comma = 0x002C +local period = 0x002E +local punctuationspace = 0x2008 + +local validseparators = { + [comma] = true, + [period] = true, + [punctuationspace] = true, +} + +local validsigns = { + [0x002B] = 0x002B, -- plus + [0x002D] = 0x2212, -- hyphen + [0x00B1] = 0x00B1, -- plusminus + [0x2212] = 0x2212, -- minus + [0x2213] = 0x2213, -- minusplus +} + +local function traced_kern(w) + return tracedrule(w,nil,nil,"darkgray") +end + +function characteralign.handler(head,where) + if not datasets then + return head, false + end + local first = first_glyph(head) -- we could do that once + if not first then + return head, false + end + local a = first[a_characteralign] + if not a or a == 0 then + return head, false + end + local column = div(a,100) + local row = a % 100 + local dataset = datasets and datasets[column] or setcharacteralign(column) + local separator = dataset.separator + local list = dataset.list + local b_start = nil + local b_stop = nil + local a_start = nil + local a_stop = nil + local c = nil + local current = first + local sign = nil + -- we can think of constraints + while current do + local id = current.id + if id == glyph_code then + local char = current.char + if char == separator then + c = current + if trace_split then + setcolor(current,"darkred") + end + elseif categories[char] == "nd" or validseparators[char] then + if c then + if not a_start then + a_start = current + end + a_stop = current + if trace_split then + setcolor(current,"darkgreen") + end + else + if not b_start then + if sign then + b_start = sign + local new = validsigns[sign.char] + if char == new or not fontcharacters[sign.font][new] then + if trace_split then + setcolor(sign,"darkyellow") + end + else + sign.char = new + if trace_split then + setcolor(sign,"darkmagenta") + end + end + sign = nil + else + b_start = current + end + end + b_stop = current + if trace_split then + setcolor(current,"darkblue") + end + end + elseif not b_start then + sign = validsigns[char] and current + end + elseif (b_start or a_start) and id == glue_code then + -- somewhat inefficient + local next = current.next + local prev = current.prev + if next and prev and next.id == glyph_code and prev.id == glyph_code then -- too much checking + local width = fontcharacters[b_start.font][period].width + -- local spec = current.spec + -- nodes.free(spec) -- hm, we leak but not that many specs + current.spec = new_gluespec(width) + current[a_character] = punctuationspace + end + end + current = current.next + end + local entry = list[row] + if entry then + if not dataset.collected then + -- print("[maxbefore] [maxafter]") + local maxbefore = 0 + local maxafter = 0 + for k, v in next, list do + local before = v.before + local after = v.after + if before and before > maxbefore then + maxbefore = before + end + if after and after > maxafter then + maxafter = after + end + end + dataset.maxafter = maxafter + dataset.maxbefore = maxbefore + dataset.collected = true + end + local maxafter = dataset.maxafter + local maxbefore = dataset.maxbefore + local before = entry.before or 0 + local after = entry.after or 0 + local new_kern = trace_split and traced_kern or new_kern + if b_start then + if before < maxbefore then + head = insert_node_before(head,b_start,new_kern(maxbefore-before)) + end + if not c then + -- print("[before]") + local width = fontcharacters[b_stop.font][separator].width + insert_node_after(head,b_stop,new_kern(maxafter+width)) + elseif a_start then + -- print("[before] [separator] [after]") + if after < maxafter then + insert_node_after(head,a_stop,new_kern(maxafter-after)) + end + else + -- print("[before] [separator]") + if maxafter > 0 then + insert_node_after(head,c,new_kern(maxafter)) + end + end + elseif a_start then + if c then + -- print("[separator] [after]") + if maxbefore > 0 then + head = insert_node_before(head,c,new_kern(maxbefore)) + end + else + -- print("[after]") + local width = fontcharacters[b_stop.font][separator].width + head = insert_node_before(head,a_start,new_kern(maxbefore+width)) + end + if after < maxafter then + insert_node_after(head,a_stop,new_kern(maxafter-after)) + end + elseif c then + -- print("[separator]") + if maxbefore > 0 then + head = insert_node_before(head,c,new_kern(maxbefore)) + end + if maxafter > 0 then + insert_node_after(head,c,new_kern(maxafter)) + end + end + else + entry = { + before = b_start and dimensions_of_list(b_start,b_stop.next) or 0, + after = a_start and dimensions_of_list(a_start,a_stop.next) or 0, + } + list[row] = entry + end + return head, true +end + +function setcharacteralign(column,separator) + if not enabled then + nodes.tasks.enableaction("processors","typesetters.characteralign.handler") + enabled = true + end + if not datasets then + datasets = { } + end + local dataset = datasets[column] -- we can use a metatable + if not dataset then + dataset = { + separator = separator and utfbyte(separator) or comma, + list = { }, + maxafter = 0, + maxbefore = 0, + collected = false, + } + datasets[column] = dataset + used = true + end + return dataset +end + +local function resetcharacteralign() + datasets = false +end + +characteralign.setcharacteralign = setcharacteralign +characteralign.resetcharacteralign = resetcharacteralign + +commands.setcharacteralign = setcharacteralign +commands.resetcharacteralign = resetcharacteralign + |