Advertisement
Guest User

Untitled

a guest
Oct 4th, 2015
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 10.28 KB | None | 0 0
  1. -- zrmakecmap.lua
  2. prog_name = "zrmakecmap"
  3. version = "0.2"
  4. mod_date = "2015/10/03"
  5. verbose = false
  6. utf32 = false
  7. ----------------------------------------
  8. filename, fontname, outname = nil
  9. tinsert, tconcat = table.insert, table.concat
  10. tunpack = unpack or table.unpack
  11. fontloader = (function(n)
  12. local r, n = pcall(require, n); return r and n
  13. end)("fontloader")
  14. ----------------------------------------
  15. do
  16. local blocksize = 100
  17. local prologue = [[
  18. %%!PS-Adobe-3.0 Resource-CMap
  19. %%%%DocumentNeededResources: ProcSet (CIDInit)
  20. %%%%IncludeResource: ProcSet (CIDInit)
  21. %%%%BeginResource: CMap (%s)
  22. %%%%Title: (%s %s %s %s)
  23. %%%%Version: %s
  24. %%%%EndComments
  25.  
  26. %% NOTICE:
  27. %% It is intended that this file is used only with dvipdfmx.
  28.  
  29. /CIDInit /ProcSet findresource begin
  30.  
  31. 12 dict begin
  32.  
  33. begincmap
  34.  
  35. /CIDSystemInfo 3 dict dup begin
  36. /Registry (%s) def
  37. /Ordering (%s) def
  38. /Supplement %s def
  39. end def
  40.  
  41. /CMapName /%s def
  42. /CMapVersion %s def
  43. /CMapType 1 def
  44.  
  45. /WMode 0 def]]
  46. local epilogue = [[
  47. endcmap
  48. CMapName currentdict /CMap defineresource pop
  49. end
  50. end
  51.  
  52. %%%%EndResource
  53. %%%%EOF
  54. ]]
  55. local function hex_utf16(uc)
  56. if uc >= 0x10000 then
  57. local h, l = math.floor(uc / 0x400), uc % 0x400
  58. uc = h * 0x10000 + l + 0xD7C0DC00
  59. return ("%08x"):format(uc)
  60. else
  61. return ("%04x"):format(uc)
  62. end
  63. end
  64. local function hex_utf32(uc, cap)
  65. return ("%08x"):format(uc)
  66. end
  67. local function rearrange(map, maxuc)
  68. maxuc = maxuc or 0x10FFFF
  69. local notdef = map[0]
  70. for uc = 1, 31 do
  71. if map[uc] ~= notdef then notdef = nil end
  72. end
  73. local minuc = notdef and 32 or 0
  74. --
  75. local ranges, suc, scc, pcc = {}
  76. for uc = minuc, maxuc do
  77. local cc = map[uc]
  78. if not (pcc and pcc + 1 == cc and uc % 256 > 0) then
  79. if pcc then
  80. tinsert(ranges, { suc, uc - 1, scc })
  81. end
  82. if cc then suc, scc = uc, cc end
  83. end
  84. pcc = cc
  85. end
  86. if pcc then
  87. tinsert(ranges, { suc, maxuc, scc })
  88. end
  89. return ranges, notdef
  90. end
  91. local function add_partition (flines, name, lines)
  92. local sl, el = 0, 0
  93. while sl < #lines do
  94. el = math.min(sl + blocksize, #lines)
  95. tinsert(flines, '')
  96. tinsert(flines, ("%s begin%s"):format(el - sl, name))
  97. for l = sl + 1, el do
  98. tinsert(flines, lines[l])
  99. end
  100. tinsert(flines, ("end%s"):format(name))
  101. sl = el
  102. end
  103. end
  104. function cmap_text(name, version, ros, map, maxuc, utf)
  105. local out = {}
  106. local rr, ro, rs = tunpack(ros)
  107. tinsert(out, prologue:format(
  108. name, name, rr, ro, rs, version, rr, ro, rs,
  109. name, version))
  110. --
  111. local hex, csrls, ndrls, ccls, crls = nil, nil, {}, {}, {}
  112. if utf == 32 then
  113. hex = hex_utf32
  114. csrls = { " <00000000> <0010FFFF>" }
  115. elseif utf == 16 then
  116. hex = hex_utf16
  117. csrls = { " <0000> <D7FF>",
  118. " <D800DC00> <DBFFDFFF>",
  119. " <E000> <FFFF>" }
  120. else sure(nil, 9)
  121. end
  122. local ranges, notdef = rearrange(map, maxuc)
  123. if notdef then
  124. ndrls[1] = ("<%s> <%s> %s"):format(hex(0), hex(31), notdef)
  125. end
  126. for i = 1, #ranges do
  127. local suc, euc, scc = tunpack(ranges[i])
  128. if suc == euc then
  129. tinsert(ccls, ("<%s> %s"):format(hex(suc), scc))
  130. else
  131. tinsert(crls,
  132. ("<%s> <%s> %s"):format(hex(suc), hex(euc), scc))
  133. end
  134. end
  135. add_partition(out, "codespacerange", csrls)
  136. add_partition(out, "notdefrange", ndrls)
  137. add_partition(out, "cidchar", ccls)
  138. add_partition(out, "cidrange", crls)
  139. tinsert(out, '')
  140. --
  141. tinsert(out, epilogue:format())
  142. return tconcat(out, "\n")
  143. end
  144. end
  145. ----------------------------------------
  146. do
  147. local stt_meta = {
  148. __tostring = function(self)
  149. return "{"..concat(self, ",").."}"
  150. end
  151. }
  152. function stt(tbl)
  153. return setmetatable(tbl, stt_meta)
  154. end
  155. function concat(tbl, ...)
  156. local t = {}
  157. for i = 1, #tbl do t[i] = tostring(tbl[i]) end
  158. return table.concat(t, ...):gsub("\n$", "")
  159. end
  160. function info(...)
  161. if not verbose then return end
  162. local t = { prog_name, ... }
  163. io.stderr:write(concat(t, ": ").."\n")
  164. end
  165. function abort(...)
  166. verbose = true; info(...)
  167. os.exit(-1)
  168. end
  169. function sure(val, a1, ...)
  170. if val then return val end
  171. if type(a1) == "number" then
  172. a1 = "error("..a1..")"
  173. end
  174. abort(a1, ...)
  175. end
  176. end
  177. ----------------------------------------
  178. do
  179. local function ver_number(version)
  180. local ver = (version or ""):gsub("(%d)_(%d)", "%1%2")
  181. local ps, pe = ver:find("^%d+%.%d+")
  182. if not ps then
  183. ps, pe = ver:find("^%d+")
  184. end
  185. ver = (ps) and ver:sub(ps, pe) or "0"
  186. if ver ~= version then
  187. info("version is not a number", version)
  188. end
  189. info("resolved version", ver)
  190. return ver
  191. end
  192. local function ros_info(cidinfo)
  193. cidinfo = cidinfo or {}
  194. if not cidinfo.registry then
  195. info("cidinfo is missing")
  196. end
  197. local res = {
  198. cidinfo.registry or "Adobe",
  199. cidinfo.ordering or "Identity",
  200. cidinfo.supplement or 0
  201. }
  202. info("resolved ROS", tconcat(res, "-"))
  203. return res
  204. end
  205. local function map_info(mapobj)
  206. mapobj = mapobj or {}
  207. sure(mapobj.map, "cannot find map data")
  208. info("obtained map data")
  209. info("*max codepoint", mapobj.encmax or "(unknown)")
  210. return mapobj.map, mapobj.encmax or 0x10FFFF
  211. end
  212. function extract_map(file, name)
  213. info("open font file", file)
  214. local font, diag
  215. if name then
  216. font, diag = fontloader.open(file, name)
  217. else
  218. font, diag = fontloader.open(file)
  219. end
  220. if diag then
  221. local max = math.min(#diag, font and 5 or 50)
  222. info(("-------- diagnoses from fontloader (%s of %s)")
  223. :format(max, #diag))
  224. for i = 1, max do info(diag[i]) end
  225. info("-------- end")
  226. end
  227. sure(font, "failure in extracing map data", fontfile)
  228. local version = ver_number(font.version)
  229. local ros = ros_info(font.cidinfo)
  230. local map, maxuc = map_info(font.map)
  231. return version, ros, map, maxuc
  232. end
  233. end
  234. ----------------------------------------
  235. do
  236. local function out_name(core)
  237. return "Uni"..core.."-UTF"..(utf32 and "32" or "16").."-H"
  238. end
  239. local function trim(name)
  240. return (name or ""):gsub("[%W]", "")
  241. end
  242. local function count(sum, name)
  243. sum[name] = (sum[name] or 0) + 1
  244. end
  245. function resolve_param()
  246. local finfo, msg = fontloader.info(filename)
  247. local ttc, sel = false, fontname
  248. if not finfo then
  249. info("-------- diagnosis from fontloader")
  250. info(msg)
  251. abort("not a valid font file", filename)
  252. elseif finfo[1] then
  253. ttc = true; info("TTC file", filename)
  254. elseif finfo.fontname then
  255. info("not TTC file", filename)
  256. sel = sel or 0; finfo = { finfo }
  257. else sure(nil, 5)
  258. end
  259. if type(sel) == "string" then
  260. local t = {}
  261. for i = 1, #finfo do
  262. if finfo[i].fontname == sel then tinsert(t, i - 1) end
  263. end
  264. for i = 1, #finfo do
  265. if finfo[i].fullname == sel then tinsert(t, i - 1) end
  266. end
  267. sure(#t <= 1, "font name is ambiguous", sel)
  268. sure(#t >= 1, "no such font in font file", sel)
  269. sel = t[1]
  270. end
  271. info("font count", #finfo)
  272. info("font index", sel or "(all)")
  273. sure(not sel or (0 <= sel and sel < #finfo),
  274. "bad font index number", sel)
  275. sure(not outname or sel,
  276. "cannot specify output file name in processing all fonts")
  277. if sel then
  278. local fi = finfo[sel + 1]
  279. return { {
  280. name = ttc and fi.fullname,
  281. out = outname or out_name(trim(fi.familyname))
  282. } }
  283. else
  284. local res, sum = {}, {}
  285. for i = 1, #finfo do
  286. local fi = finfo[i]
  287. res[i] = { name = fi.fullname, out = trim(fi.familyname) }
  288. info("font name", i, res[i].name)
  289. info("out-core", i, res[i].out)
  290. count(sum, res[i].out)
  291. end
  292. for i = 1, #finfo do
  293. if sum[res[i].out] > 1 then
  294. res[i].out = res[i].out.."_"..trim(finfo[i].weight)
  295. info("new out-core", i, res[i].out)
  296. count(sum, res[i].out)
  297. end
  298. end
  299. for i = 1, #finfo do
  300. if sum[res[i].out] > 1 then
  301. res[i].out = res[i].out.."_"..tostring(i)
  302. info("new out-core", i, res[i].out)
  303. end
  304. res[i].out = out_name(res[i].out)
  305. info("output", i, res[i].out)
  306. end
  307. return res
  308. end
  309. end
  310. end
  311. ----------------------------------------
  312. do
  313. local function show_usage()
  314. io.stdout:write(([[
  315. This is %s v%s <%s> by 'ZR'
  316. Usage: %s[.lua] [-v] [-o <out_name>] <ttc_file>
  317. -v be verbose
  318. ]]):format(prog_name, version, mod_date, prog_name))
  319. os.exit(0)
  320. end
  321. function read_option()
  322. if #arg == 0 then show_usage() end
  323. local idx = 1
  324. while idx <= #arg do
  325. local opt = arg[idx]
  326. if opt:sub(1, 1) ~= "-" then break end
  327. if opt == "-h" or opt == "--help" then
  328. show_usage()
  329. elseif opt == "-v" or opt == "--verbose" then
  330. verbose = true
  331. elseif opt == "-o" or opt == "--out" then
  332. idx = idx + 1
  333. outname = sure(arg[idx], "output name is missing")
  334. elseif opt == "-i" or opt == "--index" then
  335. idx = idx + 1
  336. fontname = tonumber(arg[idx])
  337. sure(fontname, "index is not a number")
  338. elseif opt == "-n" or opt == "--name" then
  339. idx = idx + 1
  340. fontname = sure(arg[idx], "font name is missing")
  341. elseif opt == "--utf32" then
  342. utf32 = true
  343. elseif opt == "--utf16" then
  344. utf32 = false
  345. else abort("invalid option", opt)
  346. end
  347. idx = idx + 1
  348. end
  349. sure(#arg == idx, "wrong number of arguments")
  350. filename = arg[idx]
  351. end
  352. local function write_whole(file, data)
  353. local ofile = io.open(file, "wb")
  354. sure(ofile, "cannot open for output", file)
  355. sure(ofile:write(data), "output failed", file)
  356. ofile:close()
  357. end
  358. function main()
  359. sure(fontloader, "this script requrires TeXlua")
  360. read_option()
  361. local param = resolve_param()
  362. for i = 1, #param do
  363. local name, out = param[i].name, param[i].out
  364. info("******** PROCESS", filename, name or nil)
  365. local version, ros, map, maxuc = extract_map(filename, name)
  366. info("create cmap file", out)
  367. local text = cmap_text(out, version, ros, map, maxuc,
  368. (utf32) and 32 or 16)
  369. write_whole(out, text)
  370. info("DONE", filename, name or nil)
  371. end
  372. info("all done")
  373. end
  374. end
  375. ----------------------------------------
  376. main()
  377. -- EOF
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement