Skip to content

Commit 53aea0c

Browse files
committed
Make compression optional, limit fp precision
All floating points are limited to 6 decimal digits, angles are limited to 2 decimal digits. This should reduce output file size quite a bit. Fixes #226
1 parent 034cde1 commit 53aea0c

File tree

6 files changed

+236
-117
lines changed

6 files changed

+236
-117
lines changed

InteractiveHtmlBom/core/config.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class Config:
5454
checkboxes = ','.join(default_checkboxes)
5555
bom_view = bom_view_choices[1]
5656
layer_view = layer_view_choices[1]
57+
compression = True
5758
open_browser = True
5859

5960
# General section
@@ -107,6 +108,7 @@ def load_from_ini(self):
107108
self.checkboxes = f.Read('checkboxes', self.checkboxes)
108109
self.bom_view = f.Read('bom_view', self.bom_view)
109110
self.layer_view = f.Read('layer_view', self.layer_view)
111+
self.compression = f.ReadBool('compression', self.compression)
110112
self.open_browser = f.ReadBool('open_browser', self.open_browser)
111113

112114
f.SetPath('/general')
@@ -155,6 +157,7 @@ def save(self):
155157
f.Write('checkboxes', self.checkboxes)
156158
f.Write('bom_view', self.bom_view)
157159
f.Write('layer_view', self.layer_view)
160+
f.WriteBool('compression', self.compression)
158161
f.WriteBool('open_browser', self.open_browser)
159162

160163
f.SetPath('/general')
@@ -198,6 +201,7 @@ def set_from_dialog(self, dlg):
198201
self.bom_view = self.bom_view_choices[dlg.html.bomDefaultView.Selection]
199202
self.layer_view = self.layer_view_choices[
200203
dlg.html.layerDefaultView.Selection]
204+
self.compression = dlg.html.compressionCheckbox.IsChecked()
201205
self.open_browser = dlg.html.openBrowserCheckbox.IsChecked()
202206

203207
# General
@@ -242,6 +246,7 @@ def transfer_to_dialog(self, dlg):
242246
self.bom_view)
243247
dlg.html.layerDefaultView.Selection = self.layer_view_choices.index(
244248
self.layer_view)
249+
dlg.html.compressionCheckbox.Value = self.compression
245250
dlg.html.openBrowserCheckbox.Value = self.open_browser
246251

247252
# General
@@ -321,6 +326,9 @@ def add_options(self, parser, file_name_format_hint):
321326
parser.add_argument('--layer-view', default=self.layer_view,
322327
choices=self.layer_view_choices,
323328
help='Default layer view.')
329+
parser.add_argument('--no-compression',
330+
help='Disable compression of pcb data.',
331+
action='store_true')
324332
parser.add_argument('--no-browser', help='Do not launch browser.',
325333
action='store_true')
326334

@@ -388,6 +396,7 @@ def set_from_args(self, args):
388396
self.checkboxes = args.checkboxes
389397
self.bom_view = args.bom_view
390398
self.layer_view = args.layer_view
399+
self.compression = not args.no_compression
391400
self.open_browser = not args.no_browser
392401

393402
# General

InteractiveHtmlBom/core/ibom.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -200,13 +200,28 @@ def process_substitutions(bom_name_format, pcb_file_name, metadata):
200200
return name + '.html'
201201

202202

203-
def get_compressed_pcbdata(pcbdata):
203+
def round_floats(o, precision):
204+
if isinstance(o, float):
205+
return round(o, precision)
206+
if isinstance(o, dict):
207+
return {k: round_floats(v, precision) for k, v in o.items()}
208+
if isinstance(o, (list, tuple)):
209+
return [round_floats(x, precision) for x in o]
210+
return o
211+
212+
213+
def get_pcbdata_javascript(pcbdata, compression):
204214
from .lzstring import LZString
205215

206-
pcbdata_js = LZString().compress_to_base64(json.dumps(pcbdata))
207-
pcbdata_js = json.dumps(pcbdata_js)
208-
js = "var pcbdata = JSON.parse(LZString.decompressFromBase64({}))"
209-
return js.format(pcbdata_js)
216+
js = "var pcbdata = {}"
217+
pcbdata_str = json.dumps(round_floats(pcbdata, 6))
218+
219+
if compression:
220+
log.info("Compressing pcb data")
221+
pcbdata_str = json.dumps(LZString().compress_to_base64(pcbdata_str))
222+
js = "var pcbdata = JSON.parse(LZString.decompressFromBase64({}))"
223+
224+
return js.format(pcbdata_str)
210225

211226

212227
def generate_file(pcb_file_dir, pcb_file_name, pcbdata, config):
@@ -227,28 +242,33 @@ def get_file_content(file_name):
227242
bom_file_dir = os.path.dirname(bom_file_name)
228243
if not os.path.isdir(bom_file_dir):
229244
os.makedirs(bom_file_dir)
230-
log.info("Compressing pcb data")
231-
compressed_pcbdata = get_compressed_pcbdata(pcbdata)
245+
pcbdata_js = get_pcbdata_javascript(pcbdata, config.compression)
232246
log.info("Dumping pcb data")
233247
config_js = "var config = " + config.get_html_config()
234248
html = get_file_content("ibom.html")
235249
html = html.replace('///CSS///', get_file_content('ibom.css'))
236250
html = html.replace('///USERCSS///', get_file_content('user.css'))
237251
html = html.replace('///SPLITJS///', get_file_content('split.js'))
238-
html = html.replace('///LZ-STRING///', get_file_content('lz-string.js'))
252+
html = html.replace('///LZ-STRING///',
253+
get_file_content('lz-string.js')
254+
if config.compression else '')
239255
html = html.replace('///POINTER_EVENTS_POLYFILL///',
240256
get_file_content('pep.js'))
241257
html = html.replace('///CONFIG///', config_js)
242-
html = html.replace('///PCBDATA///', compressed_pcbdata)
243258
html = html.replace('///UTILJS///', get_file_content('util.js'))
244259
html = html.replace('///RENDERJS///', get_file_content('render.js'))
245260
html = html.replace('///IBOMJS///', get_file_content('ibom.js'))
246261
html = html.replace('///USERJS///', get_file_content('user.js'))
247-
html = html.replace('///USERHEADER///', get_file_content('userheader.html'))
248-
html = html.replace('///USERFOOTER///', get_file_content('userfooter.html'))
262+
html = html.replace('///USERHEADER///',
263+
get_file_content('userheader.html'))
264+
html = html.replace('///USERFOOTER///',
265+
get_file_content('userfooter.html'))
266+
# Replace pcbdata last for better performance.
267+
html = html.replace('///PCBDATA///', pcbdata_js)
249268

250269
with io.open(bom_file_name, 'wt', encoding='utf-8') as bom:
251270
bom.write(html)
271+
252272
log.info("Created file %s", bom_file_name)
253273
return bom_file_name
254274

InteractiveHtmlBom/dialog/dialog_base.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,18 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
165165
self.layerDefaultView.SetSelection( 1 )
166166
b_sizer.Add( self.layerDefaultView, 0, wx.ALL|wx.EXPAND, 5 )
167167

168-
self.openBrowserCheckbox = wx.CheckBox( self, wx.ID_ANY, u"Open browser", wx.DefaultPosition, wx.DefaultSize, 0 )
168+
sbSizer10 = wx.StaticBoxSizer( wx.StaticBox( self, wx.ID_ANY, u"Miscellaneous" ), wx.VERTICAL )
169+
170+
self.compressionCheckbox = wx.CheckBox( sbSizer10.GetStaticBox(), wx.ID_ANY, u"Enable compression", wx.DefaultPosition, wx.DefaultSize, 0 )
171+
self.compressionCheckbox.SetValue(True)
172+
sbSizer10.Add( self.compressionCheckbox, 0, wx.ALL, 5 )
173+
174+
self.openBrowserCheckbox = wx.CheckBox( sbSizer10.GetStaticBox(), wx.ID_ANY, u"Open browser", wx.DefaultPosition, wx.DefaultSize, 0 )
169175
self.openBrowserCheckbox.SetValue(True)
170-
b_sizer.Add( self.openBrowserCheckbox, 0, wx.ALL, 5 )
176+
sbSizer10.Add( self.openBrowserCheckbox, 0, wx.ALL, 5 )
177+
178+
179+
b_sizer.Add( sbSizer10, 1, wx.EXPAND, 5 )
171180

172181

173182
self.SetSizer( b_sizer )

InteractiveHtmlBom/ecad/kicad.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ def parse_shape(self, d):
7373
"width": d.GetWidth() * 1e-6
7474
}
7575
if shape == "arc":
76-
a1 = d.GetArcAngleStart() * 0.1
77-
a2 = (d.GetArcAngleStart() + d.GetAngle()) * 0.1
76+
a1 = round(d.GetArcAngleStart() * 0.1, 2)
77+
a2 = round((d.GetArcAngleStart() + d.GetAngle()) * 0.1, 2)
7878
if d.GetAngle() < 0:
7979
(a1, a2) = (a2, a1)
8080
return {
@@ -408,8 +408,8 @@ def parse_tracks(self, tracks):
408408
else:
409409
if track.GetLayer() in [pcbnew.F_Cu, pcbnew.B_Cu]:
410410
if track.GetClass() == "ARC":
411-
a1 = track.GetArcAngleStart() * 0.1
412-
a2 = (track.GetArcAngleStart() + track.GetAngle()) * 0.1
411+
a1 = round(track.GetArcAngleStart() * 0.1, 2)
412+
a2 = round((track.GetArcAngleStart() + track.GetAngle()) * 0.1, 2)
413413
if track.GetAngle() < 0:
414414
(a1, a2) = (a2, a1)
415415
track_dict = {

InteractiveHtmlBom/ecad/svgpath.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,10 +512,13 @@ def parse_path(pathdef, logger, current_pos=0j):
512512
def create_path(lines):
513513
"""Returns a path d-string."""
514514

515+
def limit_digits(val):
516+
return format(val, '.6f').rstrip('0').replace(',', '.').rstrip('.')
517+
515518
parts = []
516519
for line in lines:
517-
parts.append('M{},{}'.format(line[0][0], line[0][1]))
520+
parts.append('M{},{}'.format(*map(limit_digits, line[0])))
518521
for point in line[1:]:
519-
parts.append('L{},{}'.format(point[0], point[1]))
522+
parts.append('L{},{}'.format(*map(limit_digits, point)))
520523

521524
return ''.join(parts)

0 commit comments

Comments
 (0)