-------------------------------------------------------------------------------
--         FILE:  luaotfload-dvi.lua
--  DESCRIPTION:  part of luaotfload / DVI
-------------------------------------------------------------------------------


assert(luaotfload_module, "This is a part of luaotfload and should not be loaded independently") { 
    name          = "luaotfload-dvi",
    version       = "3.18",       --TAGVERSION
    date          = "2021-05-21", --TAGDATE
    description   = "luaotfload submodule / DVI",
    license       = "GPL v2.0",
    author        = "Marcel Krüger",
    copyright     = "Luaotfload Development Team",  
}

local getfont = font.getfont
local setfont = node.direct.setfont
local getdisc = node.direct.getdisc
local traverse_glyph = node.direct.traverse_glyph
local traverse_id = node.direct.traverse_id
local uses_font = node.direct.uses_font
local define_font = font.define
local disc_t = node.id'disc'

-- DVI output support
--
-- When writing DVI files, we can't assume that the DVI reader has access to our
-- font dictionaries, so we need an independent representation for our glyphs. The
-- approach we chose in coordination with the dvisvgm author is to create a DVI font
-- name which indicates the font filename and some essential backend parameters
-- (especially otential extend, shrink, embolden, etc. factors) and then use GIDs in
-- the page stream.
--
-- Normally we could easily implement this using virtual fonts, but because we are
-- dealing with DVI output, LuaTeX is not evaluating virtual fonts by itself.
-- So instead, we process the shaped output and replace all glyph nodes with glyph
-- nodes from special fonts which have the characteristics expected brom the DVI reader.

-- mapped_fonts maps fontids from the user to fontids used in the DVI file
local mapped_fonts = setmetatable({}, {__index = function(t, fid)
  local font = getfont(fid)
  local mapped = font.backend_font or false
  t[fid] = mapped
  return mapped
end})

local function process(head, font)
  local mapping = mapped_fonts[font]
  if not mapping then return head end
  local mapped_font = mapping.font
  for n, c, f in traverse_glyph(head) do if f == font then
    local mapped = mapping[c]
    if mapped then setfont(n, mapped_font, mapped) end
  end end
  for n in traverse_id(disc_t, head) do if uses_font(n, font) then
    local pre, post, rep = getdisc(n)
    for n, c, f in traverse_glyph(pre) do if f == font then
      local mapped = mapping[c]
      if mapped then setfont(n, mapped_font, mapped) end
    end end
    for n, c, f in traverse_glyph(post) do if f == font then
      local mapped = mapping[c]
      if mapped then setfont(n, mapped_font, mapped) end
    end end
    for n, c, f in traverse_glyph(rep) do if f == font then
      local mapped = mapping[c]
      if mapped then setfont(n, mapped_font, mapped) end
    end end
  end end
end
local function manipulate(tfmdata, _, dvi_kind)
  if dvi_kind ~= 'dvisvgm' then
    texio.write_nl(string.format('WARNING (luaotfload): Unsupported DVI backend %q, falling back to dvisvgm.', dvi_kind))
  end
  -- Legacy fonts can be written to the DVI file directly
  if 2 ~= (tfmdata.encodingbytes or ((tfmdata.format == 'truetype' or tfmdata.format == 'opentype') and 2 or 1)) then
    return
  end
  local newfont = {}
  for k,v in pairs(tfmdata) do
    newfont[k] = v
  end
  local newchars = {}
  newfont.characters = newchars
  local lookup = {}
  for k,v in pairs(tfmdata.characters) do
    local newchar = {
      width = v.width, -- Only width should really be necessary
      height = v.height,
      depth = v.depth,
    }
    newchars[v.index or k] = newchar
    lookup[k] = v.index or k
  end
  newfont.checksum = string.unpack('>I4', 'LuaF') -- Use a magic checksum such that the reader
                                                  -- can uniquely identify these fonts.
  local name = '[' .. newfont.name .. ']:' -- TODO: Why .name? I would have expected .filename
  if newfont.subfont and newfont.subfont ~= 1 then
    name = name .. 'index=' .. tostring(math.tointeger(newfont.subfont-1)) .. ';'
  end
  if newfont.extend and newfont.extend ~= 1000 then
    name = name .. 'extend=' .. tostring(math.tointeger(math.round(newfont.extend*65.536))) .. ';'
  end
  if newfont.slant and newfont.slant ~= 0 then
    name = name .. 'slant=' .. tostring(math.tointeger(math.round(newfont.slant*65.536))) .. ';'
  end
  if newfont.mode == 2 and newfont.width and newfont.width ~= 0 then
    name = name .. 'embolden=' .. tostring(math.tointeger(math.round(newfont.width*65.78176))) .. ';'
  end
  newfont.name = name:sub(1,-2) -- Stri th trailing : or ;
  tfmdata.backend_font = lookup
  lookup.font = define_font(newfont)
end

fonts.constructors.features.otf.register {
  name = "dvifont",
  default = "dvisvgm",
  manipulators = {
    node = manipulate,
    base = manipulate,
  },
  processors = {
    node = process,
    base = process,
  },
}
