Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- zrmakecmap.lua
- prog_name = "zrmakecmap"
- version = "0.2"
- mod_date = "2015/10/03"
- verbose = false
- utf32 = false
- ----------------------------------------
- filename, fontname, outname = nil
- tinsert, tconcat = table.insert, table.concat
- tunpack = unpack or table.unpack
- fontloader = (function(n)
- local r, n = pcall(require, n); return r and n
- end)("fontloader")
- ----------------------------------------
- do
- local blocksize = 100
- local prologue = [[
- %%!PS-Adobe-3.0 Resource-CMap
- %%%%DocumentNeededResources: ProcSet (CIDInit)
- %%%%IncludeResource: ProcSet (CIDInit)
- %%%%BeginResource: CMap (%s)
- %%%%Title: (%s %s %s %s)
- %%%%Version: %s
- %%%%EndComments
- %% NOTICE:
- %% It is intended that this file is used only with dvipdfmx.
- /CIDInit /ProcSet findresource begin
- 12 dict begin
- begincmap
- /CIDSystemInfo 3 dict dup begin
- /Registry (%s) def
- /Ordering (%s) def
- /Supplement %s def
- end def
- /CMapName /%s def
- /CMapVersion %s def
- /CMapType 1 def
- /WMode 0 def]]
- local epilogue = [[
- endcmap
- CMapName currentdict /CMap defineresource pop
- end
- end
- %%%%EndResource
- %%%%EOF
- ]]
- local function hex_utf16(uc)
- if uc >= 0x10000 then
- local h, l = math.floor(uc / 0x400), uc % 0x400
- uc = h * 0x10000 + l + 0xD7C0DC00
- return ("%08x"):format(uc)
- else
- return ("%04x"):format(uc)
- end
- end
- local function hex_utf32(uc, cap)
- return ("%08x"):format(uc)
- end
- local function rearrange(map, maxuc)
- maxuc = maxuc or 0x10FFFF
- local notdef = map[0]
- for uc = 1, 31 do
- if map[uc] ~= notdef then notdef = nil end
- end
- local minuc = notdef and 32 or 0
- --
- local ranges, suc, scc, pcc = {}
- for uc = minuc, maxuc do
- local cc = map[uc]
- if not (pcc and pcc + 1 == cc and uc % 256 > 0) then
- if pcc then
- tinsert(ranges, { suc, uc - 1, scc })
- end
- if cc then suc, scc = uc, cc end
- end
- pcc = cc
- end
- if pcc then
- tinsert(ranges, { suc, maxuc, scc })
- end
- return ranges, notdef
- end
- local function add_partition (flines, name, lines)
- local sl, el = 0, 0
- while sl < #lines do
- el = math.min(sl + blocksize, #lines)
- tinsert(flines, '')
- tinsert(flines, ("%s begin%s"):format(el - sl, name))
- for l = sl + 1, el do
- tinsert(flines, lines[l])
- end
- tinsert(flines, ("end%s"):format(name))
- sl = el
- end
- end
- function cmap_text(name, version, ros, map, maxuc, utf)
- local out = {}
- local rr, ro, rs = tunpack(ros)
- tinsert(out, prologue:format(
- name, name, rr, ro, rs, version, rr, ro, rs,
- name, version))
- --
- local hex, csrls, ndrls, ccls, crls = nil, nil, {}, {}, {}
- if utf == 32 then
- hex = hex_utf32
- csrls = { " <00000000> <0010FFFF>" }
- elseif utf == 16 then
- hex = hex_utf16
- csrls = { " <0000> <D7FF>",
- " <D800DC00> <DBFFDFFF>",
- " <E000> <FFFF>" }
- else sure(nil, 9)
- end
- local ranges, notdef = rearrange(map, maxuc)
- if notdef then
- ndrls[1] = ("<%s> <%s> %s"):format(hex(0), hex(31), notdef)
- end
- for i = 1, #ranges do
- local suc, euc, scc = tunpack(ranges[i])
- if suc == euc then
- tinsert(ccls, ("<%s> %s"):format(hex(suc), scc))
- else
- tinsert(crls,
- ("<%s> <%s> %s"):format(hex(suc), hex(euc), scc))
- end
- end
- add_partition(out, "codespacerange", csrls)
- add_partition(out, "notdefrange", ndrls)
- add_partition(out, "cidchar", ccls)
- add_partition(out, "cidrange", crls)
- tinsert(out, '')
- --
- tinsert(out, epilogue:format())
- return tconcat(out, "\n")
- end
- end
- ----------------------------------------
- do
- local stt_meta = {
- __tostring = function(self)
- return "{"..concat(self, ",").."}"
- end
- }
- function stt(tbl)
- return setmetatable(tbl, stt_meta)
- end
- function concat(tbl, ...)
- local t = {}
- for i = 1, #tbl do t[i] = tostring(tbl[i]) end
- return table.concat(t, ...):gsub("\n$", "")
- end
- function info(...)
- if not verbose then return end
- local t = { prog_name, ... }
- io.stderr:write(concat(t, ": ").."\n")
- end
- function abort(...)
- verbose = true; info(...)
- os.exit(-1)
- end
- function sure(val, a1, ...)
- if val then return val end
- if type(a1) == "number" then
- a1 = "error("..a1..")"
- end
- abort(a1, ...)
- end
- end
- ----------------------------------------
- do
- local function ver_number(version)
- local ver = (version or ""):gsub("(%d)_(%d)", "%1%2")
- local ps, pe = ver:find("^%d+%.%d+")
- if not ps then
- ps, pe = ver:find("^%d+")
- end
- ver = (ps) and ver:sub(ps, pe) or "0"
- if ver ~= version then
- info("version is not a number", version)
- end
- info("resolved version", ver)
- return ver
- end
- local function ros_info(cidinfo)
- cidinfo = cidinfo or {}
- if not cidinfo.registry then
- info("cidinfo is missing")
- end
- local res = {
- cidinfo.registry or "Adobe",
- cidinfo.ordering or "Identity",
- cidinfo.supplement or 0
- }
- info("resolved ROS", tconcat(res, "-"))
- return res
- end
- local function map_info(mapobj)
- mapobj = mapobj or {}
- sure(mapobj.map, "cannot find map data")
- info("obtained map data")
- info("*max codepoint", mapobj.encmax or "(unknown)")
- return mapobj.map, mapobj.encmax or 0x10FFFF
- end
- function extract_map(file, name)
- info("open font file", file)
- local font, diag
- if name then
- font, diag = fontloader.open(file, name)
- else
- font, diag = fontloader.open(file)
- end
- if diag then
- local max = math.min(#diag, font and 5 or 50)
- info(("-------- diagnoses from fontloader (%s of %s)")
- :format(max, #diag))
- for i = 1, max do info(diag[i]) end
- info("-------- end")
- end
- sure(font, "failure in extracing map data", fontfile)
- local version = ver_number(font.version)
- local ros = ros_info(font.cidinfo)
- local map, maxuc = map_info(font.map)
- return version, ros, map, maxuc
- end
- end
- ----------------------------------------
- do
- local function out_name(core)
- return "Uni"..core.."-UTF"..(utf32 and "32" or "16").."-H"
- end
- local function trim(name)
- return (name or ""):gsub("[%W]", "")
- end
- local function count(sum, name)
- sum[name] = (sum[name] or 0) + 1
- end
- function resolve_param()
- local finfo, msg = fontloader.info(filename)
- local ttc, sel = false, fontname
- if not finfo then
- info("-------- diagnosis from fontloader")
- info(msg)
- abort("not a valid font file", filename)
- elseif finfo[1] then
- ttc = true; info("TTC file", filename)
- elseif finfo.fontname then
- info("not TTC file", filename)
- sel = sel or 0; finfo = { finfo }
- else sure(nil, 5)
- end
- if type(sel) == "string" then
- local t = {}
- for i = 1, #finfo do
- if finfo[i].fontname == sel then tinsert(t, i - 1) end
- end
- for i = 1, #finfo do
- if finfo[i].fullname == sel then tinsert(t, i - 1) end
- end
- sure(#t <= 1, "font name is ambiguous", sel)
- sure(#t >= 1, "no such font in font file", sel)
- sel = t[1]
- end
- info("font count", #finfo)
- info("font index", sel or "(all)")
- sure(not sel or (0 <= sel and sel < #finfo),
- "bad font index number", sel)
- sure(not outname or sel,
- "cannot specify output file name in processing all fonts")
- if sel then
- local fi = finfo[sel + 1]
- return { {
- name = ttc and fi.fullname,
- out = outname or out_name(trim(fi.familyname))
- } }
- else
- local res, sum = {}, {}
- for i = 1, #finfo do
- local fi = finfo[i]
- res[i] = { name = fi.fullname, out = trim(fi.familyname) }
- info("font name", i, res[i].name)
- info("out-core", i, res[i].out)
- count(sum, res[i].out)
- end
- for i = 1, #finfo do
- if sum[res[i].out] > 1 then
- res[i].out = res[i].out.."_"..trim(finfo[i].weight)
- info("new out-core", i, res[i].out)
- count(sum, res[i].out)
- end
- end
- for i = 1, #finfo do
- if sum[res[i].out] > 1 then
- res[i].out = res[i].out.."_"..tostring(i)
- info("new out-core", i, res[i].out)
- end
- res[i].out = out_name(res[i].out)
- info("output", i, res[i].out)
- end
- return res
- end
- end
- end
- ----------------------------------------
- do
- local function show_usage()
- io.stdout:write(([[
- This is %s v%s <%s> by 'ZR'
- Usage: %s[.lua] [-v] [-o <out_name>] <ttc_file>
- -v be verbose
- ]]):format(prog_name, version, mod_date, prog_name))
- os.exit(0)
- end
- function read_option()
- if #arg == 0 then show_usage() end
- local idx = 1
- while idx <= #arg do
- local opt = arg[idx]
- if opt:sub(1, 1) ~= "-" then break end
- if opt == "-h" or opt == "--help" then
- show_usage()
- elseif opt == "-v" or opt == "--verbose" then
- verbose = true
- elseif opt == "-o" or opt == "--out" then
- idx = idx + 1
- outname = sure(arg[idx], "output name is missing")
- elseif opt == "-i" or opt == "--index" then
- idx = idx + 1
- fontname = tonumber(arg[idx])
- sure(fontname, "index is not a number")
- elseif opt == "-n" or opt == "--name" then
- idx = idx + 1
- fontname = sure(arg[idx], "font name is missing")
- elseif opt == "--utf32" then
- utf32 = true
- elseif opt == "--utf16" then
- utf32 = false
- else abort("invalid option", opt)
- end
- idx = idx + 1
- end
- sure(#arg == idx, "wrong number of arguments")
- filename = arg[idx]
- end
- local function write_whole(file, data)
- local ofile = io.open(file, "wb")
- sure(ofile, "cannot open for output", file)
- sure(ofile:write(data), "output failed", file)
- ofile:close()
- end
- function main()
- sure(fontloader, "this script requrires TeXlua")
- read_option()
- local param = resolve_param()
- for i = 1, #param do
- local name, out = param[i].name, param[i].out
- info("******** PROCESS", filename, name or nil)
- local version, ros, map, maxuc = extract_map(filename, name)
- info("create cmap file", out)
- local text = cmap_text(out, version, ros, map, maxuc,
- (utf32) and 32 or 16)
- write_whole(out, text)
- info("DONE", filename, name or nil)
- end
- info("all done")
- end
- end
- ----------------------------------------
- main()
- -- EOF
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement