summaryrefslogtreecommitdiff
path: root/scripts/cmapclean.py
diff options
context:
space:
mode:
authorTor Andersson <tor.andersson@artifex.com>2018-04-04 16:36:21 +0200
committerTor Andersson <tor.andersson@artifex.com>2018-04-25 12:26:32 +0200
commita6d083bb776ecd498e57450ef84c20e39ae604cf (patch)
treea1f4399f011eb2b59e21f0588d322690a6ab14f4 /scripts/cmapclean.py
parent84cf672da90dfdaa2dfd2742cc69fa0bad268081 (diff)
downloadmupdf-a6d083bb776ecd498e57450ef84c20e39ae604cf.tar.xz
Add CMap processing scripts, and turn cmapdump into mutool.
A dumping script written in python. A flattening script written in python (for easier editing). A subsetting script written in shell to minimize CMaps by reusing subsets. Use 'mutool cmapdump' to bootstrap or verify cmap dumps.
Diffstat (limited to 'scripts/cmapclean.py')
-rw-r--r--scripts/cmapclean.py204
1 files changed, 204 insertions, 0 deletions
diff --git a/scripts/cmapclean.py b/scripts/cmapclean.py
new file mode 100644
index 00000000..f41f8afe
--- /dev/null
+++ b/scripts/cmapclean.py
@@ -0,0 +1,204 @@
+# Parse a CMap file and dump it back out.
+
+import sys
+
+# Decode a subset of CMap syntax (only what is needed for our built-in resources)
+# We require that tokens are whitespace separated.
+
+def cleancmap(filename):
+ codespacerange = []
+ usecmap = ""
+ cmapname = ""
+ cmapversion = "1.0"
+ csi_registry = "(Adobe)"
+ csi_ordering = "(Unknown)"
+ csi_supplement = 1
+ wmode = 0
+ isbf = False
+
+ map = {}
+
+ def tocode(s):
+ if s[0] == '<' and s[-1] == '>':
+ return int(s[1:-1], 16)
+ return int(s, 10)
+
+ def map_cidchar(lo, v):
+ map[lo] = v
+
+ def map_cidrange(lo, hi, v):
+ while lo <= hi:
+ map[lo] = v
+ lo = lo + 1
+ v = v + 1
+
+ def add_bf(lo, v):
+ # Decode unicode surrogate pairs
+ if len(v) == 2 and v[0] >= 0xd800 and v[0] <= 0xdbff and v[1] >= 0xdc00 and v[1] <= 0xdfff:
+ map[lo] = ((v[0] - 0xd800) << 10) + (v[1] - 0xdc00) + 0x10000
+ elif len(v) == 1:
+ map[lo] = v[0]
+ elif len(v) <= 8:
+ map[lo] = v[:]
+ else:
+ print "/* warning: too long one-to-many mapping: %s */" % (v)
+
+ def map_bfchar(lo, bf):
+ bf = bf[1:-1] # drop < >
+ v = [int(bf[i:i+4],16) for i in xrange(0, len(bf), 4)]
+ add_bf(lo, v)
+
+ def map_bfrange(lo, hi, bf):
+ bf = bf[1:-1] # drop < >
+ v = [int(bf[i:i+4],16) for i in xrange(0, len(bf), 4)]
+ while lo <= hi:
+ add_bf(lo, v)
+ lo = lo + 1
+ v[-1] = v[-1] + 1
+
+ current = None
+ for line in open(filename, "r").readlines():
+ if line[0] == '%':
+ continue
+ line = line.strip().split()
+ if len(line) == 0:
+ continue
+ if line[0] == '/CMapVersion': cmapversion = line[1]
+ elif line[0] == '/CMapName': cmapname = line[1][1:]
+ elif line[0] == '/WMode': wmode = int(line[1])
+ elif line[0] == '/Registry': csi_registry = line[1]
+ elif line[0] == '/Ordering': csi_ordering = line[1]
+ elif line[0] == '/Supplement': csi_supplement = line[1]
+ elif len(line) > 1 and line[1] == 'usecmap': usecmap = line[0][1:]
+ elif len(line) > 1 and line[1] == 'begincodespacerange': current = 'codespacerange'
+ elif len(line) > 1 and line[1] == 'begincidrange': current = 'cidrange'
+ elif len(line) > 1 and line[1] == 'beginbfrange': current = 'bfrange'; isbf = True
+ elif len(line) > 1 and line[1] == 'begincidchar': current = 'cidchar'
+ elif len(line) > 1 and line[1] == 'beginbfchar': current = 'bfchar'; isbf = True
+ elif line[0] == 'begincodespacerange': current = 'codespacerange'
+ elif line[0] == 'begincidrange': current = 'cidrange'
+ elif line[0] == 'beginbfrange': current = 'bfrange'; isbf = True
+ elif line[0] == 'begincidchar': current = 'cidchar'
+ elif line[0] == 'beginbfchar': current = 'bfchar'; isbf = True
+ elif line[0].startswith("end"):
+ current = None
+ elif current == 'codespacerange' and len(line) == 2:
+ n, a, b = (len(line[0])-2)/2, tocode(line[0]), tocode(line[1])
+ codespacerange.append((n, a, b))
+ elif current == 'cidrange' and len(line) == 3:
+ a, b, c = tocode(line[0]), tocode(line[1]), tocode(line[2])
+ map_cidrange(a, b, c)
+ elif current == 'cidchar' and len(line) == 2:
+ a, b = tocode(line[0]), tocode(line[1])
+ map_cidchar(a, b)
+ elif current == 'bfchar' and len(line) == 2:
+ a, b = tocode(line[0]), line[1]
+ map_bfchar(a, b)
+ elif current == 'bfrange' and len(line) == 3:
+ a, b, c = tocode(line[0]), tocode(line[1]), line[2]
+ map_bfrange(a, b, c)
+
+ # Create ranges
+
+ singles = []
+ ranges = []
+ mranges = []
+
+ out_lo = -100
+ out_hi = -100
+ out_v_lo = 0
+ out_v_hi = 0
+
+ def flush_range():
+ if out_lo >= 0:
+ if out_lo == out_hi:
+ singles.append((out_lo, out_v_lo))
+ else:
+ ranges.append((out_lo, out_hi, out_v_lo))
+
+ keys = map.keys()
+ keys.sort()
+ for code in keys:
+ v = map[code]
+ if type(v) is not int:
+ flush_range()
+ out_lo = out_hi = -100
+ mranges.append((code, v))
+ else:
+ if code != out_hi + 1 or v != out_v_hi + 1:
+ flush_range()
+ out_lo = out_hi = code
+ out_v_lo = out_v_hi = v
+ else:
+ out_hi = out_hi + 1
+ out_v_hi = out_v_hi + 1
+ flush_range()
+
+ # Print CMap file
+
+ print "%!PS-Adobe-3.0 Resource-CMap"
+ print "%%DocumentNeededResources: procset (CIDInit)"
+ print "%%IncludeResource: procset (CIDInit)"
+ print "%%%%BeginResource: CMap (%s)" % cmapname
+ print "%%%%Version: %s" % cmapversion
+ print "%%EndComments"
+ print "/CIDInit /ProcSet findresource begin"
+ print "12 dict begin"
+ print "begincmap"
+ if usecmap: print "/%s usecmap" % usecmap
+ print "/CIDSystemInfo 3 dict dup begin"
+ print " /Registry %s def" % csi_registry
+ print " /Ordering %s def" % csi_ordering
+ print " /Supplement %s def" % csi_supplement
+ print "end def"
+ print "/CMapName /%s def" % cmapname
+ print "/CMapVersion %s def" % cmapversion
+ print "/CMapType 1 def"
+ print "/WMode %d def" % wmode
+
+ if len(codespacerange):
+ print "%d begincodespacerange" % len(codespacerange)
+ for r in codespacerange:
+ fmt = "<%%0%dx> <%%0%dx>" % (r[0]*2, r[0]*2)
+ print fmt % (r[1], r[2])
+ print "endcodespacerange"
+
+ if len(singles) > 0:
+ if isbf:
+ print "%d beginbfchar" % len(singles)
+ for s in singles:
+ print "<%04x> <%04x>" % s
+ print "endbfchar"
+ else:
+ print "%d begincidchar" % len(singles)
+ for s in singles:
+ print "<%04x> %d" % s
+ print "endcidchar"
+
+ if len(ranges) > 0:
+ if isbf:
+ print "%d beginbfrange" % len(ranges)
+ for r in ranges:
+ print "<%04x> <%04x> <%04x>" % r
+ print "endbfrange"
+ else:
+ print "%d begincidrange" % len(ranges)
+ for r in ranges:
+ print "<%04x> <%04x> %d" % r
+ print "endcidrange"
+
+ if len(mranges) > 0:
+ print "%d beginbfchar" % len(mranges)
+ for cid, v in mranges:
+ print "<%04x> <%s>" % (cid, "".join(["%04x" % ch for ch in v]))
+ print "endbfchar"
+
+ print "endcmap"
+ print "CMapName currentdict /CMap defineresource pop"
+ print "end"
+ print "end"
+ print "%%EndResource"
+ print "%%EOF"
+
+for arg in sys.argv[1:]:
+ cleancmap(arg)