>
> It might be a linux shell piping issue, since your are second large
> number of file using >  destination file I noticed.
>
> I made sure the input latex file I gave it try_1.tex is not too
> large, so not to run into the dvisvgm overflow problem. This file
> has only 1,000 pages and may be 5,000 equations.

I've made a new version of dvisvgm-hashes.lua. It compresses
consecutive page ranges, so it shouldn't crate such long page lists.
Moreover, if GNU Make is available on the system, it makes a Makefile,
which calls dvisvgm only on subset of pages (64 by default) and it
calls it in parallel (thanks to Reinhard for this idea). It should
speed up the compilation in theory, although I am not sure from my
tests that it really works in practise.

>
> When I tried it on a really small file, it worked.  _BUT_ and
> this is a big _BUT_, some of the math generated looks really
> bad but some look like normal SVG. Here is screen shot
>
> https://www.12000.org/tmp/08182018/image.png
>
> Looking at the HTML, it seems some math was not really
> converted to SVG at all, while other was.

I can see it. I am not sure what is the issue, maybe Martin know? The
call to dvisvgm is in the form:

dvisvgm -v 4 -n --exact -c 1.15,1.15 -p 1-5 filename.idv 2> filename.dlog

>
> --------------------------------
> <td
> class="align-label">
> </td></tr><tr><td
> class="align-odd"><span
> class="cmex-10x-x-109">∫</span>
>    <span
> class="cmr-10x-x-109">d</span><span
> class="cmmi-10x-x-109">y</span></td>
> --------------------------
>
> What is the above doing in the HTML? Should it not be an image?
>

You are missing the `pic-align` option I think.

Best regards,
Michal
local dvireader = require "dvireader"
local mkutils = require "mkutils"
local filter = require "make4ht-filter"

-- mapping between tex4ht image names and hashed image names
local output_map = {}
local dvisvgm_options = "-n --exact -c 1.15,1.15"
local parallel_size = 64
-- local parallel_size = 3

local function make_hashed_name(base, hash)
  return base .. "-" ..hash..".svg"
end



-- process output of dvisvgm and find output page numbers and corresponding files
local function get_generated_pages(output, pages)
  local pages = pages or {}
  local pos = 1
  local pos, finish, page = string.find(output, "processing page (%d+)", pos)
  while(pos) do
    pos, finish, file = string.find(output, "output written to ([^\n]+)", finish)
    pages[tonumber(page)] = file
    pos, finish, page = string.find(output, "processing page (%d+)", finish)
  end
  return pages
end

local function make_ranges(pages)
  local newpages = {}
  local start, stop
  for i=1,#pages do
    local current = pages[i]
    local next_el = pages[i+1] or current + 100 -- just select a big number
    local diff = next_el - current
    if diff == 1 then
      if not start then start = current end
    else
      local element
      if start then
        element = start .. "-" .. current
      else
        element = current
      end
      newpages[#newpages+1] = element
      start = nil
    end
  end
  return newpages
end

local function read_log(dvisvgmlog)
  local f = io.open(dvisvgmlog, "r")
  if not f then return nil, "Cannot read dvisvgm log" end
  local output = f:read("*all")
  f:close()
  return output
end

-- test the existence of GNU Make, which can execute tasks in parallel
local function test_make()
  local make = io.popen("make -v", "r")
  if not make then return false end
  local content = make:read("*all")
  make:close()
  return content:match("GNU Make")
end

local function save_file(filename, text)
  local f = io.open(filename, "w")
  f:write(text) 
  f:close()
end


local function make_makefile_command(idvfile, page_sequences)
  local logs = {}
  local all = {} -- list of targets in the "all:" makefile target
  local targets = {}
  local basename = idvfile:gsub(".idv$", "")
  local makefilename = basename .. "-images" .. ".mk"
  -- build make targets
  for i, ranges in ipairs(page_sequences) do
    local target = basename .. "-" .. i
    local logfile = target .. ".dlog"
    logs[#logs + 1] = logfile
    all[#all+1] = target
    local chunk = target .. ":\n\tdvisvgm -v4" .. dvisvgm_options .. " -p " .. ranges  .. " " .. idvfile .. " 2> " .. logfile .. "\n"
    targets[#targets + 1] = chunk
  end
  -- construct makefile and save it
  local makefile = "all: " .. table.concat(all, " ") .. "\n\n" .. table.concat(targets, "\n")
  save_file(makefilename, makefile)
  local command = "make -j -f " .. makefilename
  return command, logs
end

local function prepare_command(idvfile, pages)
  local logs = {}
  if #pages > parallel_size and test_make() then 
    local page_sequences = {}
    for i=1, #pages, parallel_size do
      local current_pages = {}
      for x = i, i+parallel_size -1 do
        current_pages[#current_pages + 1] = pages[x]
      end
      table.insert(page_sequences,table.concat(make_ranges(current_pages), ","))
    end
    return make_makefile_command(idvfile, page_sequences)
  end
  -- else
    local pagesequence = table.concat(make_ranges(pages), ",")
    -- the stderr from dvisvgm must be redirected and postprocessed
    local dvisvgmlog = idvfile:gsub("idv$", "dlog")
    -- local dvisvgm = io.popen("dvisvgm -v4 -n --exact -c 1.15,1.15 -p " .. pagesequence .. " " .. idvfile, "r")
    local command = "dvisvgm -v4 " .. dvisvgm_options .. " -p " .. pagesequence .. " " .. idvfile .. " 2> " .. dvisvgmlog
    return command, {dvisvgmlog}
  -- end
end

local function execute_dvisvgm(idvfile, pages)
  if #pages < 1 then return nil, "No pages to convert" end
  local command, logs = prepare_command(idvfile, pages)
  print(command)
  os.execute(command)
  local generated_pages = {}
  for _, dvisvgmlog in ipairs(logs) do
    local output = read_log(dvisvgmlog)
    generated_pages = get_generated_pages(output, generated_pages)
  end
  return generated_pages
end

local function get_dvi_pages(arg)
  -- list of pages to convert in this run
  local to_convert = {}
  local idv_file = arg.input .. ".idv"
  -- set extension options
  local extoptions = mkutils.get_filter_settings "dvisvgm-hashes" or {}
  dvisvgm_options = arg.options or extoptions.options or dvisvgm_options
  parallel_size = arg.parallel_size or extoptions.parallel_size or parallel_size
  local f = io.open(idv_file, "r")
  if not f then return nil, "Cannot open idv file: " .. idv_file end
  local content = f:read("*all")
  f:close()
  local dvi_pages = dvireader.get_pages(content)
  -- we must find page numbers and output name sfor the generated images
  local lg = mkutils.parse_lg(arg.input ..".lg")
  for _, name in ipairs(lg.images) do
    local page = tonumber(name.page)
    local hash = dvi_pages[page]
    local tex4ht_name = name.output
    local output_name = make_hashed_name(arg.input, hash)
    output_map[tex4ht_name] = output_name
    if not mkutils.file_exists(output_name) then
      print(output_name)
      to_convert[#to_convert+1] = page
    end
  end
  local generated_files, msg = execute_dvisvgm(idv_file, to_convert)
  if not generated_files then
    return nil, msg
  end

  -- rename the generated files to the hashed filenames
  for page, file in pairs(generated_files) do
    os.rename(file, make_hashed_name(arg.input, dvi_pages[page]))
  end

end

-- this must be used in the .mk4 file as
-- Make:dvisvgm_hashes {}
Make:add("dvisvgm_hashes", function(arg)
  get_dvi_pages(arg)
end, {
  parallel_size=parallel_size -- number of pages which should be concetanated before 
})

-- replace original image names with hashed names
local executed = false
Make:match(".*", function(arg)
  if not executed then
    executed = true
    local lgfiles = Make.lgfile.files
    for i, filename in ipairs(lgfiles) do
      local replace = output_map[filename]
      if replace then
        lgfiles[i] = replace
      end
     end
  end
end)

-- fix src attributes
local process = filter {
  function(str)
    return str:gsub('src="([^"]+)', function(filename)
      local newname = output_map[filename] or filename
      print("newname", newname)
      return 'src="'.. newname 
    end)
  end
}
  
Make:match("html$", process)

-- disable the image processing
Make:image(".", function() return "" end)

Reply via email to