if not modules then modules = { } end modules ['typo-drp'] = { version = 1.001, comment = "companion to typo-drp.mkiv", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } -- This one is sensitive for order (e.g. when combined with first line -- processing. -- todo: use isglyph local tonumber, type, next = tonumber, type, next local ceil = math.ceil local settings_to_hash = utilities.parsers.settings_to_hash local trace_initials = false trackers.register("typesetters.initials", function(v) trace_initials = v end) local report_initials = logs.reporter("nodes","initials") local initials = typesetters.paragraphs or { } typesetters.initials = initials or { } local nodes = nodes local tasks = nodes.tasks local enableaction = tasks.enableaction local disableaction = tasks.disableaction local nuts = nodes.nuts local tonut = nodes.tonut local getnext = nuts.getnext local getprev = nuts.getprev local getchar = nuts.getchar local getid = nuts.getid local getattr = nuts.getattr local getwhd = nuts.getwhd local getprop = nuts.getprop local setprop = nuts.setprop local setattr = nuts.setattr local setlink = nuts.setlink local setprev = nuts.setprev local setnext = nuts.setnext local setfont = nuts.setfont local setchar = nuts.setchar local setwhd = nuts.setwhd local setkern = nuts.setkern local setoffsets = nuts.setoffsets local setglyphdata = nuts.setglyphdata local setattr = nuts.setattr local hpack_nodes = nuts.hpack local nodecodes = nodes.nodecodes local nodepool = nuts.pool local new_kern = nodepool.kern local insertbefore = nuts.insertbefore local insertafter = nuts.insertafter local remove_node = nuts.remove local startofpar = nuts.startofpar local nextnode = nuts.traversers.node local nextglyph = nuts.traversers.glyph local variables = interfaces.variables local v_default = variables.default local v_margin = variables.margin local v_auto = variables.auto local v_first = variables.first local v_keep = variables.keep local v_last = variables.last local texget = tex.get local texset = tex.set local unsetvalue = attributes.unsetvalue local glyph_code = nodecodes.glyph local hlist_code = nodecodes.hlist local glue_code = nodecodes.glue local kern_code = nodecodes.kern local par_code = nodecodes.par local actions = { } initials.actions = actions local a_initial = attributes.private("initial") local a_color = attributes.private('color') local a_transparency = attributes.private('transparency') local a_colormodel = attributes.private('colormodel') local category = characters.category local function set(par,specification) enableaction("processors","typesetters.initials.handler") if trace_initials then report_initials("enabling initials") end setprop(par,a_initial,specification) end function initials.set(specification) nuts.setparproperty(set,specification) end interfaces.implement { name = "setinitial", actions = initials.set, arguments = { { { "location" }, { "enabled", "boolean" }, { "method" }, { "distance" ,"dimen" }, { "hoffset" ,"dimen" }, { "voffset" ,"dimen" }, { "font", "integer" }, { "dynamic", "integer" }, { "ca", "integer" }, { "ma", "integer" }, { "ta", "integer" }, { "n", "integer" }, { "m", "integer" }, } } } -- todo: prevent linebreak .. but normally a initial ends up at the top of -- a page so this has a low priority actions[v_default] = function(head,setting) local skip = false -- begin of par local first = getnext(head) local indent = false -- parbox .. needs to be set at 0 if first and getid(first) == hlist_code then first = getnext(first) indent = true end -- we need to skip over kerns and glues (signals) while first and getid(first) ~= glyph_code do first = getnext(first) end if first and getid(first) == glyph_code then local ma = setting.ma or 0 local ca = setting.ca local ta = setting.ta local last = first local distance = setting.distance or 0 local voffset = setting.voffset or 0 local hoffset = setting.hoffset or 0 local parindent = texget("parindent") local baseline = texget("baselineskip",false) local lines = tonumber(setting.n) or 0 local dynamic = setting.dynamic local font = setting.font local method = settings_to_hash(setting.method) local length = tonumber(setting.m) or 1 -- -- 1 char | n chars | skip first quote | ignore punct | keep punct -- if getattr(first,a_initial) then for current in nextnode, getnext(first) do if getattr(current,a_initial) then last = current else break end end elseif method[v_auto] then local char = getchar(first) local kind = category(char) if kind == "po" or kind == "pi" then if method[v_first] then -- remove quote etc before initial local next = getnext(first) if not next then -- don't start with a quote or so return head end last = nil for current in nextglyph, next do head, first = remove_node(head,first,true) first = current last = first break end if not last then -- no following glyph or so return head end else -- keep quote etc with initial local next = getnext(first) if next and method[v_keep] then skip = first end if not next then -- don't start with a quote or so return head end for current in nextglyph, next do last = current break end if last == first then return head end end elseif kind == "pf" then -- error: final quote else -- okay end -- maybe also: get all A. B. etc local next = getnext(first) if next then for current, char in nextglyph, next do local kind = category(char) if kind == "po" then if method[v_last] then -- remove period etc after initial remove_node(head,current,true) else -- keep period etc with initial last = current end end break end end else for current in nextglyph, first do last = current if length <= 1 then break else length = length - 1 end end end local current = first while true do local id = getid(current) if id == kern_code then setkern(current,0) elseif id == glyph_code and skip ~= current then local next = getnext(current) if font then setfont(current,font) end if dynamic > 0 then setglyphdata(current,dynamic) end -- can be a helper if ca and ca > 0 then setattr(current,a_colormodel,ma == 0 and 1 or ma) setattr(current,a_color,ca) end if ta and ta > 0 then setattr(current,a_transparency,ta) end -- end if current == last then break else current = getnext(current) end end -- We pack so that successive handling cannot touch the dropped cap. Packaging -- in a hlist is also needed because we cannot locally adapt e.g. parindent (not -- yet stored in with par). local prev = getprev(first) local next = getnext(last) -- setprev(first) setnext(last) local dropper = hpack_nodes(first) local width, height, depth = getwhd(dropper) setwhd(dropper,0,0,0) -- setlink(prev,dropper) setlink(dropper,next) -- if next then local current = next while current do local id = getid(current) if id == glue_code or id == kern_code then local next = getnext(current) -- remove_node(current,current,true) -- created an invalid next link and dangling remains remove_node(head,current,true) current = next else break end end end -- local hoffset = width + hoffset + distance + (indent and parindent or 0) for current in nextglyph, first do if skip == current then setoffsets(current,-hoffset,0) else setoffsets(current,-hoffset,-voffset) -- no longer - height here end if current == last then break end end -- first = dropper -- if setting.location == v_margin then -- okay else if lines == 0 then -- safeguard, not too precise lines = ceil((height+voffset) / baseline) end -- We cannot set parshape yet ... when we can I'll add a slope -- option (positive and negative, in emwidth). local hangafter = - lines local hangindent = width + distance if trace_initials then report_initials("setting hangafter to %i and hangindent to %p",hangafter,hangindent) end texset("hangafter",hangafter) texset("hangindent",hangindent) end if indent then insertafter(first,first,new_kern(-parindent)) end end return head end -- we can count ... when all done, we can disable ... function initials.handler(head) if getid(head) == par_code and startofpar(head) then local settings = getprop(head,a_initial) if settings then disableaction("processors","typesetters.initials.handler") local alternative = settings.alternative or v_default local action = actions[alternative] or actions[v_default] if action then if trace_initials then report_initials("processing initials, alternative %a",alternative) end return action(head,settings) end end end return head end