Skip to content

Commit

Permalink
Merge pull request #748 from nguyen-v/inc_table_stackup_font
Browse files Browse the repository at this point in the history
[Draw Fancy Stackup][Include Table][Added] Support custom fonts
  • Loading branch information
set-soft authored Dec 24, 2024
2 parents ebb6f8c + 73b7b8e commit 3156316
Show file tree
Hide file tree
Showing 5 changed files with 1,313 additions and 1,275 deletions.
7 changes: 6 additions & 1 deletion docs/source/configuration/outputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,7 @@ the image aspect ratio.
Creating a group in your PCB
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Variuos outputs and preflights uses a PCB group to specify the position and
Various outputs and preflights uses a PCB group to specify the position and
size of the target object to be placed in your PCB. Here we explain how to
create a group.

Expand All @@ -938,3 +938,8 @@ create a group.
- Now edit the group and change its name according to the name specified by
the output or preflight.

.. note::

For the `Draw Fancy Stackup` and `Include Table` preflights, the font can be
set by adding to the group a textbox with the desired font. You can leave the
textbox empty and hide the borders.
25 changes: 22 additions & 3 deletions kibot/kicad/pcb_draw_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
# Helper functions to draw on a BOARD object
from ..gs import GS
import pcbnew
import re
if GS.ki7:
# Is this change really needed??!!! People doesn't have much to do ...
GR_TEXT_HJUSTIFY_LEFT = pcbnew.GR_TEXT_H_ALIGN_LEFT
Expand Down Expand Up @@ -64,22 +65,38 @@ def draw_line(g, x1, y1, x2, y2, layer, line_w=10000):
GS.board.Add(nl)


def draw_text(g, x, y, text, h, w, layer, bold=False, alignment=GR_TEXT_HJUSTIFY_LEFT):
def draw_text(g, x, y, text, h, w, layer, bold=False, alignment=GR_TEXT_HJUSTIFY_LEFT, font=None):
nt = pcbnew.PCB_TEXT(GS.board)
nt.SetText(text)
nt.SetBold(bold)

nt.SetTextX(x)
nt.SetTextY(y+h)
nt.SetLayer(layer)
nt.SetTextWidth(w)
nt.SetTextHeight(h)
nt.SetHorizJustify(alignment)
nt.SetVertJustify(GR_TEXT_VJUSTIFY_CENTER)
if font:
# segfault if overbars (~{text}) with custom fonts
remove_overbars(nt)
nt.SetFont(font)
if bold:
nt.SetBold(bold)

g.AddItem(nt)
GS.board.Add(nt)

return nt, nt.GetTextBox().GetWidth()


def remove_overbars(txt):
current_text = txt.GetText()

if current_text:
cleaned_text = re.sub(r'~\{(.*?)\}', r'\1', current_text)
txt.SetText(cleaned_text)


def draw_poly(g, points, layer, filled=False, line_w=10000):
assert not points or len(points) < 3, "A polygon requires at least 3 points"
sps = pcbnew.SHAPE_POLY_SET()
Expand All @@ -97,10 +114,12 @@ def draw_poly(g, points, layer, filled=False, line_w=10000):
GS.board.Add(ps)


def get_text_width(text, w=10000, bold=False):
def get_text_width(text, w=10000, bold=False, font=None):
nt = pcbnew.PCB_TEXT(GS.board)
nt.SetText(text)
nt.SetBold(bold)
if font:
nt.SetFont(font)
nt.SetTextWidth(w)
width = nt.GetTextBox().GetWidth()
return width
36 changes: 21 additions & 15 deletions kibot/pre_draw_fancy_stackup.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,15 +385,15 @@ def analyze_stackup(ops, gerber):
return stackup


def meassure_table(ops, gerber, via_layer_pairs, stackup):
def measure_table(ops, gerber, via_layer_pairs, stackup, font=None):
# Set the maximum length of each column to the column title for now
for c in ops._columns:
c.title = TITLES[c.type]
c.max_len = get_text_width(c.title)
c.max_len = get_text_width(c.title, font=font)
if c.type == 'gerber' and gerber == {}:
c.max_len = 0

col_spacing_width = get_text_width('o')*ops.column_spacing
col_spacing_width = get_text_width('o', font=font)*ops.column_spacing

# Compute maximum width of each column according to stackup data
for c in ops._columns:
Expand Down Expand Up @@ -434,12 +434,18 @@ def meassure_table(ops, gerber, via_layer_pairs, stackup):


def update_drawing_group(g, pos_x, pos_y, width, tlayer, ops, gerber, via_layer_pairs):
# Purge all content

font = None

for item in g.GetItems():
GS.board.Delete(item)
if not isinstance(item, pcbnew.PCB_TEXTBOX):
GS.board.Delete(item)
else:
font = item.GetFont()

# Analyze the stackup
stackup = analyze_stackup(ops, gerber)
meassure_table(ops, gerber, via_layer_pairs, stackup)
measure_table(ops, gerber, via_layer_pairs, stackup, font)

# Draw the stackup
total_char_w = sum(c.width_char for c in ops._columns) + ops.column_spacing
Expand Down Expand Up @@ -480,17 +486,17 @@ def update_drawing_group(g, pos_x, pos_y, width, tlayer, ops, gerber, via_layer_
for c in ops._columns:
bold = (layer.material == "Copper")
if c.type == 'material':
draw_text(g, c.x, y, layer.material, font_w, font_w, tlayer, bold)
draw_text(g, c.x, y, layer.material, font_w, font_w, tlayer, bold, font=font)
elif c.type == 'layer':
draw_text(g, c.x, y, layer.layer, font_w, font_w, tlayer, bold)
draw_text(g, c.x, y, layer.layer, font_w, font_w, tlayer, bold, font=font)
elif c.type == 'thickness':
draw_text(g, c.x, y, layer.thickness, font_w, font_w, tlayer, bold)
draw_text(g, c.x, y, layer.thickness, font_w, font_w, tlayer, bold, font=font)
elif c.type == 'dielectric':
draw_text(g, c.x, y, layer.dielectric, font_w, font_w, tlayer, bold)
draw_text(g, c.x, y, layer.dielectric, font_w, font_w, tlayer, bold, font=font)
elif c.type == 'layer_type':
draw_text(g, c.x, y, layer.layer_type, font_w, font_w, tlayer, bold)
draw_text(g, c.x, y, layer.layer_type, font_w, font_w, tlayer, bold, font=font)
elif c.type == 'gerber':
draw_text(g, c.x, y, layer.gerber, font_w, font_w, tlayer, bold)
draw_text(g, c.x, y, layer.gerber, font_w, font_w, tlayer, bold, font=font)
y += row_h
if layer.material == "Core":
y += core_extra_padding
Expand All @@ -508,16 +514,16 @@ def update_drawing_group(g, pos_x, pos_y, width, tlayer, ops, gerber, via_layer_

# Draw text titles
for c in ops._columns:
draw_text(g, c.x, int(pos_y + font_w/2), c.title, font_w, font_w, tlayer)
draw_text(g, c.x, int(pos_y + font_w/2), c.title, font_w, font_w, tlayer, font=font)

# Draw thickness
ds = GS.board.GetDesignSettings()
draw_text(g, table_x, int(pos_y + table_h + font_w/2), f"Total thickness: {GS.to_mm(ds.GetBoardThickness())}mm",
font_w, font_w, tlayer)
font_w, font_w, tlayer, font=font)

# Draw note
if ops.note != '':
draw_text(g, table_x, int(pos_y + table_h + font_w/2 + row_h), "Note: " + ops.note, font_w, font_w, tlayer)
draw_text(g, table_x, int(pos_y + table_h + font_w/2 + row_h), "Note: " + ops.note, font_w, font_w, tlayer, font=font)

if not ops.draw_stackup:
return True
Expand Down
26 changes: 17 additions & 9 deletions kibot/pre_include_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import os
import csv
import re
import pcbnew
from .error import KiPlotConfigurationError
from .gs import GS
from .kicad.pcb_draw_helpers import (draw_rect, draw_line, draw_text, get_text_width,
Expand Down Expand Up @@ -117,8 +118,13 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice
if not os.path.isfile(csv_file):
raise KiPlotConfigurationError(f'Missing `{csv_file}`, create it first using the `{out.name}` output')

font = None

for item in g.GetItems():
GS.board.Delete(item)
if not isinstance(item, pcbnew.PCB_TEXTBOX):
GS.board.Delete(item)
else:
font = item.GetFont()

cols = []

Expand Down Expand Up @@ -151,7 +157,7 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice
if out.invert_columns_order:
cols.reverse()

measure_table(cols, out)
measure_table(cols, out, font=font)

total_char_w = sum(c.width_char for c in cols)
total_rel_w = sum((c.width for c in cols))
Expand Down Expand Up @@ -179,8 +185,8 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice
y += int(row_h)
draw_line(g, pos_x, y, pos_x + width, y, tlayer, line_w=GS.from_mm(out.header_rule_width))
for c in cols:
draw_text(g, c.x + c.xoffset, int(pos_y + 0.5 * row_h - font_w), c.header, font_w, font_w,
tlayer, bold=out.bold_headers, alignment=out._text_alignment)
txt, _ = draw_text(g, c.x + c.xoffset, int(pos_y + 0.5 * row_h - font_w), c.header, font_w, font_w,
tlayer, bold=out.bold_headers, alignment=out._text_alignment, font=font)

for i in range(max_row_data - 1):
rule_y = int(y + (i + 1) * row_h)
Expand All @@ -190,7 +196,8 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice
for c in cols:
row_y = int(y + row_h / 2)
for d in c.data:
draw_text(g, c.x + c.xoffset, int(row_y - font_w), d, font_w, font_w, tlayer, alignment=out._text_alignment)
txt, _ = draw_text(g, c.x + c.xoffset, int(row_y - font_w), d, font_w, font_w,
tlayer, alignment=out._text_alignment, font=font)
row_y += row_h
table_h = int(max(table_h, row_y - pos_y) - row_h / 2)

Expand All @@ -205,13 +212,13 @@ def update_table_group(g, pos_x, pos_y, width, tlayer, ops, out, csv_file, slice
draw_rect(g, pos_x, pos_y, width, table_h, tlayer, line_w=GS.from_mm(out.border_width))


def measure_table(cols, out):
col_spacing_width = get_text_width('o')*out.column_spacing
def measure_table(cols, out, font=None):
col_spacing_width = get_text_width('o', font=font)*out.column_spacing

for c in cols:
max_data_len = max(get_text_width(d) for d in c.data) if c.data else 0
max_data_len = max(get_text_width(d, font=font) for d in c.data) if c.data else 0
max_data_width_char = max(len(d) for d in c.data) if c.data else 0
c.max_len = max(get_text_width(c.header), max_data_len) + col_spacing_width
c.max_len = max(get_text_width(c.header, font=font), max_data_len) + col_spacing_width
c.width_char = max(len(c.header), max_data_width_char) + out.column_spacing

tot_len = sum(c.max_len for c in cols)
Expand Down Expand Up @@ -306,6 +313,7 @@ def update_table(ops, parent):
f' with name {g.GetName()}')

update_table_group(g, x1, y1, x2 - x1, layer, ops, out, csv[index], slice_str)

updated = True

if not group_found:
Expand Down
Loading

0 comments on commit 3156316

Please sign in to comment.