-
Notifications
You must be signed in to change notification settings - Fork 0
/
fixupSVG.py
164 lines (147 loc) · 5.17 KB
/
fixupSVG.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#This Python tool performs a heuristic trim of an SVG file.
#It's intended for use before applying the IETF svgcheck
#tool that checks and fixes up an SVG file for use in RFCs
#according to RFC7996.
#It's hacked together for convenient use on my Windows
#setup, not as part of a toolchain, and it uses simple
#pattern matching in the SVG, rather than a full parse.
#This tool does several things:
#
#1. Any <?xml ...> or <!DOCTYPE ...> declarations are removed,
# because they seem to upset the IETF toolchain.
#
#2. Colour or grey-scale fills and strokes are changed to black
# or white. Lighter fills are changed to white, and darker
# ones to black. In many cases, this will be helpful for
# diagrams prepared for another purpose but used in an RFC.
# If the result is not OK, it will be necessary to fix the
# the offending colors in the original drawing tool.
#
#3. Remove 'stroke="none"' and 'stroke:none'
# because they seem to upset the IETF toolchain.
#
#4. Remove 'width' and 'height' from 'viewbox'.
# This assists proper scaling when viewing the final
# html file with most browsers.
#
#5. NOT fixed up here: RFC7996 disallows fill using a URL
# for the pattern; I also have a hack that allows this
# if and only if the URL points to an embedded pattern
# (embedded in the SVG file itself, i.e 'url(#xxxx)'
import fileinput
import time
def fix_rgb(l):
"""Force color to monochrome"""
#This function calls itself recursively to fix the whole line
threshold = 381 #arbitrary choice for black/white threshold
if 'fill="rgb(' in l or 'stroke="rgb(' in l:
#rgb color code
if 'fill="rgb(' in l:
element = 'fill'
else:
element = 'stroke'
l1,l2 = l.split(element+'="rgb(',1)
triple,l3 = l2.split(')"',1)
if '%' in triple:
#percentage rgb values
t = eval(triple.replace('%',''))
shade = int(t[0]*255/100) + int(t[1]*255/100) + int(t[2]*255/100)
else:
#decimal rgb values
shade = sum(eval(triple))
if shade > threshold:
#make it white
lout = l1 + element+'="white"' +l3
else:
#make it black
lout = l1 + element+'="black"' +l3
print("Fixed rgb")
return(fix_rgb(lout))
elif 'fill="#' in l or 'stroke="#' in l:
#hexadecimal color code
if 'fill="#' in l:
element = 'fill'
else:
element = 'stroke'
l1,l2 = l.split(element+'="#',1)
code = l2[:6] #just the 6 hex digits
l3 = l2[7:] #not including the closing "
shade = int(code[0:2],16) + int(code[2:4],16) + int(code[4:6],16)
if shade > threshold:
#make it white
lout = l1 + element+'="white"' +l3
else:
#make it black
lout = l1 + element+'="black"' +l3
print("Fixed #color")
return(fix_rgb(lout))
elif 'fill:#' in l or 'stroke:#' in l:
#hexadecimal color code from Inkscape
if 'fill:#' in l:
element = 'fill'
else:
element = 'stroke'
l1,l2 = l.split(element+':#',1)
code = l2[:6] #just the 6 hex digits
l3 = l2[6:] #there is no closing "
shade = int(code[0:2],16) + int(code[2:4],16) + int(code[4:6],16)
if shade > threshold:
#make it white
lout = l1 + element+':white' +l3
else:
#make it black
lout = l1 + element+':black' +l3
print("Fixed :#color")
return(fix_rgb(lout))
else:
return(l)
def nostroke(l):
"""Remove stroke="none"""
#This function calls itself recursively to fix the whole line
if 'stroke="none"' in l:
l1,l2 = l.split('stroke="none"',1)
print('Removed stroke="none"')
return(nostroke(l1+l2))
elif 'stroke:none' in l:
l1,l2 = l.split('stroke:none',1)
print('Removed stroke:none')
return(nostroke(l1+l2))
else:
return(l)
def nowh(l):
"""Remove width and height to allow scaling"""
if 'viewBox' in l:
print("Seen viewbox")
if ' width="' in l:
l1,l2 = l.split(' width="',1)
_,l3 = l2.split('"',1)
lout= l1 + l3
print("Removed width")
else:
lout = l
if ' height="' in lout:
l1,l2 = lout.split(' height="',1)
_,l3 = l2.split('"',1)
lout= l1 + l3
print("Removed height")
return(lout)
else:
return(l)
#This is pure laziness because standard output
#just isn't convenient on Windows
fout = 'C:/brian/docs/temp/fixed.svg'
trimmed = open(fout, "w")
print('Fixing up standard input file')
for line in fileinput.input():
if (not '<?xml' in line) and (not '<!DOCTYPE' in line):
#line is useful
l = fix_rgb(line)
l = nostroke(l)
l = nowh(l)
trimmed.write(l)
#Yes, OK, trimmed.write(nowh(nostroke(fix_rgb(line)))) would work too.
trimmed.close()
print('Fixed file is', fout)
#This gives time to read the outputs, because on Windows
#your window just goes away on exit.
time.sleep(10)