Skip to content

Commit

Permalink
Handle font_data in genericjson parser
Browse files Browse the repository at this point in the history
  • Loading branch information
Funkenjaeger authored and qu1ck committed Jan 14, 2023
1 parent 6567e9a commit 6063d50
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 13 deletions.
4 changes: 3 additions & 1 deletion InteractiveHtmlBom/core/config.py
Expand Up @@ -37,7 +37,8 @@ class Config:
html_config_fields = [
'dark_mode', 'show_pads', 'show_fabrication', 'show_silkscreen',
'highlight_pin1', 'redraw_on_drag', 'board_rotation', 'checkboxes',
'bom_view', 'layer_view', 'offset_back_rotation'
'bom_view', 'layer_view', 'offset_back_rotation',
'kicad_text_formatting'
]
default_show_group_fields = ["Value", "Footprint"]

Expand Down Expand Up @@ -67,6 +68,7 @@ class Config:
blacklist_empty_val = False
include_tracks = False
include_nets = False
kicad_text_formatting = True

# Extra fields section
extra_data_file = None
Expand Down
20 changes: 19 additions & 1 deletion InteractiveHtmlBom/ecad/common.py
Expand Up @@ -111,11 +111,29 @@ def add_polygon():
for point in polygon:
bbox.add_point(point[0], point[1])

def add_arc():
if 'svgpath' in drawing:
add_svgpath()
else:
width = drawing.get('width', 0)
xc, yc = drawing['start'][:2]
a1 = drawing['startangle']
a2 = drawing['endangle']
r = drawing['radius']
x1 = xc + math.cos(math.radians(a1))
y1 = xc + math.sin(math.radians(a1))
x2 = xc + math.cos(math.radians(a2))
y2 = xc + math.sin(math.radians(a2))
da = a2 - a1 if a2 > a1 else a2 + 360 - a1
la = 1 if da > 180 else 0
svgpath = f'M {x1} {y1} A {r} {r} 0 {la} 1 {x2} {y2}'
bbox.add_svgpath(svgpath, width, self.logger)

{
'segment': add_segment,
'rect': add_segment, # bbox of a rect and segment are the same
'circle': add_circle,
'arc': add_svgpath,
'arc': add_arc,
'polygon': add_polygon,
'text': lambda: None, # text is not really needed for bounding box
}.get(drawing['type'])()
Expand Down
52 changes: 52 additions & 0 deletions InteractiveHtmlBom/ecad/genericjson.py
Expand Up @@ -4,6 +4,8 @@
from jsonschema import validate, ValidationError

from .common import EcadParser, Component, BoundingBox
from ..core.fontparser import FontParser
from ..errors import ParsingException


class GenericJsonParser(EcadParser):
Expand Down Expand Up @@ -65,6 +67,45 @@ def _verify(self, pcb):

return True

@staticmethod
def _texts(pcbdata):
for layer in pcbdata['drawings'].values():
for side in layer.values():
for dwg in side:
if 'text' in dwg:
yield dwg

@staticmethod
def _remove_control_codes(s):
import unicodedata
return ''.join(c for c in s if unicodedata.category(c)[0] != "C")

def _parse_font_data(self, pcbdata):
font_parser = FontParser()
for dwg in self._texts(pcbdata):
if 'svgpath' not in dwg:
dwg['text'] = self._remove_control_codes(dwg['text'])
font_parser.parse_font_for_string(dwg['text'])

if font_parser.get_parsed_font():
pcbdata['font_data'] = font_parser.get_parsed_font()

def _check_font_data(self, pcbdata):
mc = set()
for dwg in self._texts(pcbdata):
dwg['text'] = self._remove_control_codes(dwg['text'])
mc.update({c for c in dwg['text'] if 'svgpath' not in dwg and
c not in pcbdata['font_data']})

if mc:
s = ''.join(mc)
self.logger.error('Provided font_data is missing character(s)'
f' "{s}" that are present in text drawing'
' objects')
return False
else:
return True

def _parse(self):
try:
pcb = self.get_generic_json_pcb()
Expand All @@ -82,6 +123,15 @@ def _parse(self):
pcbdata = pcb['pcbdata']
components = [Component(**c) for c in pcb['components']]

if 'font_data' in pcbdata:
if not self._check_font_data(pcbdata):
raise ParsingException(f'Failed parsing {self.file_name}')
else:
self._parse_font_data(pcbdata)
if 'font_data' in pcbdata:
self.logger.info('No font_data provided in JSON, using '
'newstroke font')

self.logger.info('Successfully parsed {}'.format(self.file_name))

return pcbdata, components
Expand All @@ -108,4 +158,6 @@ def parse(self):
c.extra_fields = {
f: c.extra_fields.get(f, "") for f in extra_fields}

self.config.kicad_text_formatting = False

return pcbdata, components
23 changes: 23 additions & 0 deletions InteractiveHtmlBom/ecad/schema/genericjsonpcbdata_v1.schema
Expand Up @@ -88,6 +88,9 @@
"nets": {
"type": "array",
"items": { "type": "string" }
},
"font_data": {
"$ref": "#/definitions/FontData"
}
},
"required": [
Expand Down Expand Up @@ -592,6 +595,9 @@
}
}
},
"PolyLineArray": {
"$ref": "#/definitions/Polygons"
},
"ReferenceSet": {
"type": "array",
"items": {
Expand All @@ -609,6 +615,23 @@
"properties": {
},
"title": "ExtraData"
},
"FontData": {
"type": "object",
"patternProperties": {
"^.$" : {
"type": "object",
"properties": {
"w": { "type": "number" },
"l": { "$ref": "#/definitions/PolyLineArray" }
},
"additionalProperties" : false,
"required": [
"w",
"l"
]
}
}
}
}
}
22 changes: 12 additions & 10 deletions InteractiveHtmlBom/web/render.js
Expand Up @@ -71,16 +71,18 @@ function drawText(ctx, text, color) {
var offsetx = -lineWidth * (text.justify[0] + 1) / 2;
var inOverbar = false;
for (var j = 0; j < txt[i].length; j++) {
if (txt[i][j] == '\t') {
var fourSpaces = 4 * pcbdata.font_data[' '].w * text.width;
offsetx += fourSpaces - offsetx % fourSpaces;
continue;
} else if (txt[i][j] == '~') {
j++;
if (j == txt[i].length)
break;
if (txt[i][j] != '~') {
inOverbar = !inOverbar;
if (config.kicad_text_formatting) {
if (txt[i][j] == '\t') {
var fourSpaces = 4 * pcbdata.font_data[' '].w * text.width;
offsetx += fourSpaces - offsetx % fourSpaces;
continue;
} else if (txt[i][j] == '~') {
j++;
if (j == txt[i].length)
break;
if (txt[i][j] != '~') {
inOverbar = !inOverbar;
}
}
}
var glyph = pcbdata.font_data[txt[i][j]];
Expand Down
2 changes: 1 addition & 1 deletion InteractiveHtmlBom/web/util.js
Expand Up @@ -488,7 +488,7 @@ var settings = {
renderTracks: true,
renderZones: true,
columnOrder: [],
hiddenColumns: [],
hiddenColumns: []
}

function initDefaults() {
Expand Down

0 comments on commit 6063d50

Please sign in to comment.