New populate output

- The PcbDraw command
This commit is contained in:
Salvador E. Tropea 2022-10-27 21:24:57 -03:00
parent 8d3696796b
commit e56343d214
9 changed files with 20462 additions and 3 deletions

207
kibot/PcbDraw/mdrenderer.py Normal file
View File

@ -0,0 +1,207 @@
# coding: utf-8
"""
Markdown renderer
~~~~~~~~~~~~~~~~~
This class renders parsed markdown back to markdown.
It is useful for automatic modifications of the md contents.
:copyright: (c) 2015 by Jaroslav Kysela
(c) extended by Jan Mrázek 2022
:licence: WTFPL 2
"""
# The following try-catch is used to support mistune 0.8.4 and 2.x
try:
from mistune.renderers import BaseRenderer # type: ignore
except ModuleNotFoundError:
from mistune import Renderer # type: ignore
BaseRenderer = Renderer
class MdRenderer(BaseRenderer):
def __init__(self, **kwargs):
super(MdRenderer, self).__init__()
def get_block(text):
type = text[0]
p = text.find(':')
if p <= 0:
return ('', '', '')
l = int(text[1:p])
t = text[p+1:p+1+l]
return (text[p+1+l:], type, t)
def newline(self):
return '\n'
def text(self, text):
return text
def linebreak(self):
return '\n'
def hrule(self):
return '---\n'
def heading(self, text, level, raw=None):
return '#'*level + " " + text + '\n\n'
# Mistune 0.8.4 calls it header, not heading
header = heading
def paragraph(self, text):
return text + '\n\n'
def list(self, text, ordered=True):
r = ''
while text:
text, type, t = MdRenderer.get_block(text)
if type == 'l':
r += (ordered and ('# ' + t) or ('* ' + t)) + '\n'
return r
def list_item(self, text):
return 'l' + str(len(text)) + ':' + text
def block_text(self, text):
return text
def block_code(self, code, lang=None):
return '```\n' + code + '\n```\n'
def block_quote(self, text):
r = ''
for line in text.splitlines():
r += (line and '> ' or '') + line + '\n'
return r
def _emphasis(self, text, pref):
return pref + text + pref + ' '
def emphasis(self, text):
return self._emphasis(text, '_')
def double_emphasis(self, text):
return self._emphasis(text, '__')
def strikethrough(self, text):
return self._emphasis(text, '~~')
def codespan(self, text):
return '`' + text + '`'
def autolink(self, link, is_email=False):
return '<' + link + '>'
def link(self, link, title, text, image=False):
r = (image and '!' or '') + '[' + text + '](' + link + ')'
if title:
r += '"' + title + '"'
return r
def image(self, src, title, text):
self.link(src, title, text, image=True)
def table(self, header, body):
hrows = []
while header:
header, type, t = MdRenderer.get_block(header)
if type == 'r':
flags = {}
cols = []
while t:
t, type2, t2 = MdRenderer.get_block(t)
if type2 == 'f':
fl, v = t2.split('=')
flags[fl] = v
elif type2 == 'c':
cols.append(
type('', (object,), {'flags': flags, 'text': t2})())
hrows.append(cols)
brows = []
while body:
body, type, t = MdRenderer.get_block(body)
if type == 'r':
flags = {}
cols = []
while t:
t, type2, t2 = MdRenderer.get_block(t)
if type2 == 'f':
fl, v = t2.split('=')
flags[fl] = v
elif type2 == 'c':
cols.append(
type('', (object,), {'flags': flags, 'text': t2})())
brows.append(cols)
colscount = 0
colmax = [0] * 100
align = [''] * 100
for row in hrows + brows:
colscount = max(len(row), colscount)
i = 0
for col in row:
colmax[i] = max(len(col.text), colmax[i])
if 'align' in col.flags:
align[i] = col.flags['align'][0]
i += 1
r = ''
for row in hrows:
i = 0
for col in row:
if i > 0:
r += ' | '
r += col.text.ljust(colmax[i])
i += 1
r += '\n'
for i in range(colscount):
if i > 0:
r += ' | '
if align[i] == 'c':
r += ':' + '-'.ljust(colmax[i]-2, '-') + ':'
elif align[i] == 'l':
r += ':' + '-'.ljust(colmax[i]-1, '-')
elif align[i] == 'r':
r += '-'.ljust(colmax[i]-1, '-') + ':'
else:
r += '-'.ljust(colmax[i], '-')
r += '\n'
for row in brows:
i = 0
for col in row:
if i > 0:
r += ' | '
r += col.text.ljust(colmax[i])
i += 1
r += '\n'
return r
def table_row(self, content):
return 'r' + str(len(content)) + ':' + content
def table_cell(self, content, **flags):
content = content.replace('\n', ' ')
r = ''
for fl in flags:
v = flags[fl]
if type(v) == type(True):
v = v and 1 or 0
v = str(v) and str(v) or ''
r += 'f' + str(len(fl) + 1 + len(v)) + ':' + fl + '=' + v
return r + 'c' + str(len(content)) + ':' + content
def footnote_ref(self, key, index):
return '[^' + str(index) + ']'
def footnote_item(self, key, text):
r = '[^' + str(index) + ']:\n'
for l in text.split('\n'):
r += ' ' + l.lstrip().rstrip() + '\n'
return r
def footnotes(self, text):
return text
def finalize(self, data):
return ''.join(data)

350
kibot/PcbDraw/populate.py Normal file
View File

@ -0,0 +1,350 @@
import codecs
import os
import re
import shlex
import sys
from copy import deepcopy
from itertools import chain
from typing import List, Optional, Any, Tuple, Dict
import mistune # type: ignore
# The following try-catch is used to support mistune 0.8.4 and 2.x
try:
from mistune.plugins.table import plugin_table # type: ignore
from mistune.plugins.footnotes import plugin_footnotes # type: ignore
InlineParser = mistune.inline_parser.InlineParser
HTMLRenderer = mistune.renderers.HTMLRenderer
except ModuleNotFoundError:
InlineParser = mistune.InlineLexer
HTMLRenderer = mistune.Renderer
import pybars # type: ignore
import yaml
from . import mdrenderer
from .plot import find_data_file, get_global_datapaths
PKG_BASE = os.path.dirname(__file__)
def parse_pcbdraw(lexer: Any, m: re.Match[str], state: Any=None) -> Any:
text = m.group(1)
side, components = text.split("|")
components = list(map(lambda x: x.strip(), components.split(",")))
return 'pcbdraw', side, components
class PcbDrawInlineLexer(InlineParser): # type: ignore
def __init__(self, renderer: Any, **kwargs: Any) -> None:
super(PcbDrawInlineLexer, self).__init__(renderer, **kwargs)
self.enable_pcbdraw()
def enable_pcbdraw(self) -> None:
pcbdraw_pattern = (
r"\[\[" # [[
r"([\s\S]+?\|[\s\S]+?)" # side| component
r"\]\](?!\])" # ]]
)
if hasattr(self, 'register_rule'):
# mistune v2 API
self.rules.insert(3, 'pcbdraw')
self.register_rule('pcbdraw', pcbdraw_pattern, parse_pcbdraw)
else:
# mistune v0.8.4
self.rules.pcbdraw = re.compile(pcbdraw_pattern)
self.default_rules.insert(3, 'pcbdraw')
# This method is invoked by the old mistune API (i.e. v0.8.4)
# For the new API we register `parse_pcbdraw`
def output_pcbdraw(self, m: re.Match[str]) -> Any:
_, side, components = parse_pcbdraw(self, m)
return self.renderer.pcbdraw(side, components)
def Renderer(BaseRenderer, initial_components: List[str]): # type: ignore
class Tmp(BaseRenderer): # type: ignore
def __init__(self, initial_components: List[str]) -> None:
super(Tmp, self).__init__(escape=False)
self.items: List[Dict[str, Any]]= []
self.current_item: Optional[Dict[str, Any]] = None
self.active_side: str = "front"
self.visited_components: List[str] = initial_components
self.active_components: List[str] = []
def append_comment(self, html: str) -> None:
if self.current_item is not None and self.current_item["type"] == "steps":
self.items.append(self.current_item)
if self.current_item is None or self.current_item["type"] == "steps":
self.current_item = {
"is_comment": True,
"type": "comment",
"content": ""
}
self.current_item["content"] += html
def append_step(self, step: Dict[str, Any]) -> None:
if self.current_item is not None and self.current_item["type"] == "comment":
self.items.append(self.current_item)
if self.current_item is None or self.current_item["type"] == "comment":
self.current_item = {
"is_step": True,
"type": "steps",
"steps": []
}
self.current_item["steps"].append(step)
def output(self) -> List[Dict[str, Any]]:
items = self.items
if self.current_item is not None:
items.append(self.current_item)
return items
def pcbdraw(self, side: str, components: List[str]) -> str:
self.active_side = side
self.visited_components += components
self.active_components = components
return ""
def block_code(self, children: str, info: Optional[str]=None) -> Any:
retval = super(Tmp, self).block_code(children, info)
self.append_comment(retval)
return retval
def block_quote(self, text: str) -> Any:
retval = super(Tmp, self).block_quote(text)
self.append_comment(retval)
return retval
def block_html(self, html: str) -> Any:
retval = super(Tmp, self).block_html(html)
self.append_comment(retval)
return retval
def heading(self, children: str, level: int) -> Any:
retval = super(Tmp, self).heading(children, level)
self.append_comment(retval)
return retval
# Mistune 0.8.4 API
def header(self, text: str, level: int, raw: Optional[str]=None) -> Any:
retval = super(Tmp, self).header(text, level, raw)
self.append_comment(retval)
return retval
# Mistune 0.8.4 API
def hrule(self) -> Any:
retval = super(Tmp, self).hrule()
self.append_comment(retval)
return retval
def thematic_break(self) -> Any:
retval = super(Tmp, self).thematic_break()
self.append_comment(retval)
return retval
def list(self, text: Any, ordered: bool, level: Any=None, start: Any=None) -> str:
return ""
def list_item(self, text: str, level: Any=None) -> str:
step = {
"side": self.active_side,
"components": self.visited_components,
"active_components": self.active_components,
"comment": text
}
self.append_step(deepcopy(step))
return ""
def paragraph(self, text: str) -> Any:
retval = super(Tmp, self).paragraph(text)
self.append_comment(retval)
return retval
def table(self, header: str, body: str) -> Any:
retval = super(Tmp, self).table(header, body)
self.append_comment(retval)
return retval
return Tmp(initial_components)
def load_content(filename: str) -> Tuple[Optional[Dict[str, Any]], str]:
header = None
with codecs.open(filename, encoding="utf-8") as f:
content = f.read()
if content.startswith("---"):
end = content.find("...")
if end != -1:
header = yaml.safe_load(content[3:end])
content = content[end+3:]
return header, content
def parse_content(renderer: Any, content: str) -> List[Dict[str, Any]]:
lexer = PcbDrawInlineLexer(renderer)
processor = mistune.Markdown(renderer=renderer, inline=lexer)
try:
plugin_table(processor)
plugin_footnotes(processor)
except NameError:
# Mistune v0.8.4 doesn't define the above functions
pass
processor(content)
return renderer.output() # type: ignore
def read_template(filename: str) -> str:
with codecs.open(filename, encoding="utf-8") as f:
return f.read()
def generate_html(template: str, input: List[Dict[str, Any]]) -> bytes:
input_dict = {
"items": input
}
template_fn = pybars.Compiler().compile(template)
return template_fn(input_dict).encode("utf-8") # type: ignore
def generate_markdown(input: List[Dict[str, Any]]) -> bytes:
output = ""
for item in input:
if item["type"] == "comment":
output += item["content"] + "\n"
else:
for x in item["steps"]:
output += "#### " + x["comment"] + "\n\n"
output += "![step](" + x["img"] + ")\n\n"
return output.encode("utf-8")
def generate_images(content: List[Dict[str, Any]], boardfilename: str,
plot_args: List[str], name: str, outdir: str) -> List[Dict[str, Any]]:
dir = os.path.dirname(os.path.join(outdir, name))
if not os.path.exists(dir):
os.makedirs(dir)
counter = 0
for item in content:
if item["type"] != "steps":
continue
for x in item["steps"]:
counter += 1
filename = name.format(counter)
generate_image(boardfilename, x["side"], x["components"],
x["active_components"], plot_args, os.path.join(outdir, filename))
x["img"] = filename
return content
def generate_image(boardfilename: str, side: str, components: List[str],
active: List[str], plot_args: List[str], outputfile: str) -> None:
from copy import deepcopy
from .ui import plot
plot_args = deepcopy(plot_args)
if side.startswith("back"):
plot_args += ["--side", "back"]
plot_args += ["--filter", ",".join(components)]
plot_args += ["--highlight", ",".join(active)]
plot_args += [boardfilename, outputfile]
try:
plot.main(args=plot_args)
except SystemExit as e:
if e.code is not None and e.code != 0:
raise e from None
def get_data_path() -> List[str]:
paths: List[str] = []
paths += filter(lambda x: len(x) > 0, os.environ.get("PCBDRAW_LIB_PATH", "").split(":"))
paths += [os.path.join(PKG_BASE, "resources", "templates")]
paths += get_global_datapaths()
return paths
def prepare_params(params: List[str]) -> List[str]:
p = [shlex.split(x) for x in params]
return list(chain(*p))
def create_renderer(format, initial_components):
if format == "html":
return Renderer(HTMLRenderer, initial_components) # type: ignore
return Renderer(mdrenderer.MdRenderer, initial_components) # type: ignore
# @click.command()
# @click.argument("input", type=click.Path(exists=True, file_okay=True, dir_okay=False))
# @click.argument("output", type=click.Path(file_okay=False, dir_okay=True))
# @click.option("--board", "-b", type=click.Path(exists=True, file_okay=True, dir_okay=False),
# default=None, help="override input board")
# @click.option("--imgname", "-t", type=str, default=None,
# help="overide image name template, should contain exactly one {{}}")
# @click.option("--template", "-t", type=str, default=None,
# help="override handlebars template for HTML output")
# @click.option("--type", "-t", type=click.Choice(["md", "html"]), default=None,
# help="override output type: markdown or HTML")
# def populate(input: str, output: str, board: Optional[str], imgname: Optional[str],
# template: Optional[str], type: Optional[str]) -> None:
# """
# Create assembly step-by-step guides
# """
#
# app = fakeKiCADGui()
#
# data_path = get_data_path()
# try:
# header, content = load_content(input)
# except IOError:
# sys.exit("Cannot open source file " + input)
#
# # We change board and output paths to absolute; then we change working
# # directory to the input file so we resolve everything according to it
# if board is not None:
# board = os.path.realpath(board)
# outputpath = os.path.realpath(output)
# input_dir = os.path.dirname(input)
# if input_dir != '':
# os.chdir(input_dir)
#
# # If no overriding is specified, load it from the template
# try:
# if board is None:
# if header is None:
# raise KeyError("board")
# board = header["board"]
# if imgname is None:
# if header is None:
# raise KeyError("imgname")
# imgname = header["imgname"]
# if type is None:
# if header is None:
# raise KeyError("type")
# type = header["type"]
# if template is None and type == "html":
# if header is None:
# raise KeyError("template")
# template = header["template"]
# except KeyError as e:
# sys.exit(f"Missing parameter {e} either in template file or source header")
#
# if type == "html":
# renderer = Renderer(HTMLRenderer, header.get("initial_components", [])) # type: ignore
# outputfile = "index.html"
# try:
# assert template is not None
# template_file = find_data_file(template, '.handlebars', data_path)
# if template_file is None:
# raise RuntimeError(f"Cannot find template '{template}'")
# template = read_template(template_file)
# except IOError:
# sys.exit("Cannot open template file " + str(template))
# else:
# renderer = Renderer(pcbdraw.mdrenderer.MdRenderer, header.get("initial_components", [])) # type: ignore
# outputfile = "index.md"
# parsed_content = parse_content(renderer, content)
# if header is None:
# raise RuntimeError("Parameters were not specified in the template")
# parsed_content = generate_images(parsed_content, board, prepare_params(header["params"]),
# imgname, outputpath)
# if type == "html":
# assert template is not None
# output_content = generate_html(template, parsed_content)
# else:
# output_content = generate_markdown(parsed_content)
#
# with open(os.path.join(outputpath, outputfile), "wb") as f:
# f.write(output_content)
#
# if __name__ == '__main__':
# populate()

View File

@ -160,7 +160,8 @@ class BaseOutput(RegOutput):
def run(self, output_dir):
self.output_dir = output_dir
self.options.run(self.expand_filename(output_dir, self.options.output))
output = self.options.output if hasattr(self.options, 'output') else ''
self.options.run(self.expand_filename(output_dir, output))
class BoMRegex(Optionable):

View File

@ -277,8 +277,8 @@ class PcbDrawOptions(VariantOptions):
# Libs
if isinstance(self.libs, type):
self.libs = ['KiCAD-base']
else:
self.libs = ','.join(self.libs)
# else:
# self.libs = ','.join(self.libs)
# V-CUTS layer
self._vcuts_layer = Layer.solve(self.vcuts_layer)[0]._id if self.vcuts else 41
# Highlight
@ -420,6 +420,7 @@ class PcbDrawOptions(VariantOptions):
plotter.setup_env_data_path()
# Libs from the user HOME and the system (for pcbdraw)
plotter.setup_global_data_path()
logger.debugl(3, 'PcbDraw data path: {}'.format(plotter.data_path))
plotter.yield_warning = pcbdraw_warnings
plotter.libs = self.libs
plotter.render_back = self.bottom

201
kibot/out_populate.py Normal file
View File

@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0
# Project: KiBot (formerly KiPlot)
"""
Dependencies:
- name: mistune
python_module: true
debian: python3-mistune
arch: python-mistune
role: mandatory
"""
import os
from tempfile import NamedTemporaryFile
# Here we import the whole module to make monkeypatch work
from .error import KiPlotConfigurationError
from .misc import W_PCBDRAW
from .gs import GS
from .kiplot import config_output, run_output
from .optionable import Optionable
from .out_base import VariantOptions
from .PcbDraw.populate import (load_content, get_data_path, read_template, create_renderer, parse_content, generate_html,
generate_markdown)
from .PcbDraw.plot import find_data_file
from .registrable import RegOutput
from .macros import macros, document, output_class # noqa: F401
from . import log
logger = log.get_logger()
def pcbdraw_warnings(tag, msg):
logger.warning('{}({}) {}'.format(W_PCBDRAW, tag, msg))
def _get_tmp_name(ext):
with NamedTemporaryFile(mode='w', suffix=ext, delete=False) as f:
f.close()
return f.name
class PopulateOptions(VariantOptions):
def __init__(self):
with document:
self.renderer = ''
""" *Name of the output used to render the PCB steps.
Currently this must be a `pcbdraw` output """
self.template = 'simple'
""" [string] The name of the handlebars template used for the HTML output.
The extension must be `.handlebars`, it will be added when missing.
The `simple.handlebars` template is a built-in template """
self.format = 'html'
""" *[html,md] Format for the generated output """
self.initial_components = Optionable
""" [string|list(string)=''] List of components soldered before the first step """
self.input = ''
""" *Name of the input file describing the assembly. Must be a markdown file.
Note that the YAML section of the file will be skipped, all the needed information
comes from this output and the `renderer` output """
self.imgname = 'img/populating_%d.%x'
""" Pattern used for the image names. The `%d` is replaced by the image number.
The `%x` is replaced by the extension. Note that the format is selected by the
`renderer` """
super().__init__()
def config(self, parent):
super().config(parent)
# Validate the input file name
if not self.input:
raise KiPlotConfigurationError('You must specify an input markdown file')
if not os.path.isfile(self.input):
raise KiPlotConfigurationError('Missing input file `{}`'.format(self.input))
# Load the template
if self.format == 'html':
data_path = get_data_path()
data_path.insert(0, os.path.join(GS.get_resource_path('pcbdraw'), 'templates'))
template_file = find_data_file(self.template, '.handlebars', data_path)
if not template_file:
raise KiPlotConfigurationError('Unable to find template file `{}`'.format(self.template))
try:
self._template = read_template(template_file)
except IOError:
raise KiPlotConfigurationError('Failed to load file `{}`'.format(template_file))
# Initial components
self.initial_components = Optionable.force_list(self.initial_components)
# Validate the image patter name
if '%d' not in self.imgname:
raise KiPlotConfigurationError('The image pattern must contain `%d` `{}`'.format(self.imgname))
# def get_targets(self, out_dir):
# return [self._parent.expand_filename(out_dir, self.output)]
def generate_image(self, side, components, active_components, name):
options = self._renderer.options
logger.debug('Starting renderer with side: {}, components: {}, high: {}, image: {}'.
format(side, components, active_components, name))
# Configure it according to our needs
options.bottom = side.startswith("back")
options.show_components = [c for c in components if c]
if not options.show_components:
options.show_components = None
options.add_to_variant = False
options.highlight = [c for c in active_components if c]
options.output = name
self._renderer.dir = self._parent.dir
self._renderer._done = False
run_output(self._renderer)
return options.expand_filename_both(name, is_sch=False)
def save_options(self):
""" Save the current renderer settings """
options = self._renderer.options
self.old_bottom = options.bottom
self.old_show_components = options.show_components
self.old_add_to_variant = options.add_to_variant
self.old_highlight = options.highlight
self.old_output = options.output
self.old_dir = self._renderer.dir
self.old_done = self._renderer._done
def restore_options(self):
""" Restore the renderer settings """
options = self._renderer.options
options.bottom = self.old_bottom
options.show_components = self.old_show_components
options.add_to_variant = self.old_add_to_variant
options.highlight = self.old_highlight
options.output = self.old_output
self._renderer.dir = self.old_dir
self._renderer._done = self.old_done
def generate_images(self, dir_name, content):
# Memorize the current options
self.save_options()
dir = os.path.dirname(os.path.join(dir_name, self.imgname))
if not os.path.exists(dir):
os.makedirs(dir)
counter = 0
for item in content:
if item["type"] != "steps":
continue
for x in item["steps"]:
counter += 1
filename = self.imgname.replace('%d', str(counter))
x["img"] = self.generate_image(x["side"], x["components"], x["active_components"], filename)
# Restore the options
self.restore_options()
return content
def run(self, dir_name):
is_html = self.format == 'html'
# Check the renderer output is valid
out = RegOutput.get_output(self.renderer)
if out is None:
raise KiPlotConfigurationError('Unknown output `{}` selected in {}'.format(self.renderer, self._parent))
config_output(out)
self._renderer = out
# Load the input content
try:
_, content = load_content(self.input)
except IOError:
raise KiPlotConfigurationError('Failed to load `{}`'.format(self.input))
# Initialize the output file renderer
renderer = create_renderer(self.format, self.initial_components)
outputfile = 'index.html' if is_html else 'index.md'
# Parse the input markdown
parsed_content = parse_content(renderer, content)
logger.debugl(3, parsed_content)
# Generate the images
self.generate_images(dir_name, parsed_content)
# Generate the output file content
if is_html:
output_content = generate_html(self._template, parsed_content)
else:
output_content = generate_markdown(parsed_content)
logger.debugl(3, output_content)
# Write it to the output file
with open(os.path.join(dir_name, outputfile), "wb") as f:
f.write(output_content)
@output_class
class Populate(BaseOutput): # noqa: F821
""" Populate - Assembly instructions builder
Creates a markdown and/or HTML file explaining how to assembly a PCB.
Each step shows the already soldered components and the ones to add highlighted.
This is equivalent to the PcbDraw's Populate command, but integrated to KiBot.
For more information about the input markdown file please consult the PcbDraw project """
def __init__(self):
super().__init__()
with document:
self.options = PopulateOptions
""" *[dict] Options for the `populate` output """
self._category = 'PCB/docs'
# def get_dependencies(self):
# @staticmethod
# def get_conf_examples(name, layers, templates):

View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Populating manual</title>
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" rel="stylesheet">
<style>
figure {
padding: 1em;
background: rgb(218, 241, 255);
border-radius: 3px;
}
body {
padding-bottom: 3em;
}
</style>
</head>
<body>
<div class="container">
{{#each items}}
<div class="row">
{{#if this.is_comment}}
<div class="col-md-12">
{{{this.content}}}
</div>
{{/if}}
{{#if this.is_step}}
{{#each this.steps}}
<div class="col-lg-6">
<figure class="figure">
<img src="{{this.img}}" class="figure-img img-fluid rounded">
<figcaption class="figure-caption">{{{this.comment}}}</figcaption>
</figure>
</div>
{{/each}}
{{/if}}
</div>
{{/each}}
</div>
<scrip src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.bundle.min.js">
</body>
</html>

File diff suppressed because it is too large Load Diff

36
tests/data/source_html.md Normal file
View File

@ -0,0 +1,36 @@
---
params:
- --remap remap.json
- --libs KiCAD-base
imgname: img/populating_{}.png
template: simple
type: html
board: ../resources/ArduinoLearningKitStarter.kicad_pcb
initial_components:
- C1
- R13
...
# Demo population manual
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Itaque earum rerum hic
tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias
consequatur aut perferendis doloribus asperiores repellat. Vestibulum fermentum
tortor id mi. Nulla turpis magna, cursus sit amet, suscipit a, interdum id,
felis.
- [[front | ]] This is the front side of the board we are populating
- [[back | ]] This is the back side of the board we are populating
- [[front | RV1, RV2 ]] First, populate RV1 and RV2. Basically, any description
could be here.
- [[front | U2 ]] Let's populate U2!
You can put a paragraph of text between the population steps. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Itaque earum rerum hic tenetur a
sapiente.
- [[back | R24 ]] We can also populate a component on the other side
## Conclusion
This is the end of the demo.

View File

@ -0,0 +1,78 @@
kiplot:
version: 1
outputs:
- name: PcbDraw
comment: "PcbDraw test top"
type: pcbdraw
dir: PcbDraw
run_by_default: false
options: &pcb_draw_ops
format: png
style:
board: "#1b1f44"
copper: "#00406a"
silk: "#d5dce4"
pads: "#cfb96e"
clad: "#72786c"
outline: "#000000"
vcut: "#bf2600"
highlight_on_top: false
highlight_style: "stroke:none;fill:#ff0000;opacity:0.5;"
highlight_padding: 1.5
#libs:
# - default
# - eagle-default
remap:
L_G1: "LEDs:LED-5MM_green"
L_B1: "LEDs:LED-5MM_blue"
L_Y1: "LEDs:LED-5MM_yellow"
'REF**': "dummy:dummy"
G***: "dummy:dummy"
svg2mod: "dummy:dummy"
JP1: "dummy:dummy"
JP2: "dummy:dummy"
JP3: "dummy:dummy"
JP4: "dummy:dummy"
remap_components:
- ref: PHOTO1
lib: yaqwsx
comp: R_PHOTO_7mm
- reference: J8
library: yaqwsx
component: Pin_Header_Straight_1x02_circle
no_drillholes: False
mirror: False
highlight:
- L_G1
- L_B1
- R10
- RV1
show_components: all
vcuts: True
warnings: visible
dpi: 600
# margin:
# left: 5
# right: 1
# top: 0
# bottom: 6
# outline_width: 3
# show_solderpaste: false
resistor_remap:
- ref: R1
val: 10K
- ref: R2
val: 4k7
resistor_flip: "R2"
size_detection: svg_paths
# size_detection: kicad_all
# size_detection: kicad_edge
- name: Populate
comment: "Populate example"
type: populate
dir: Populate
options:
renderer: PcbDraw
input: tests/data/source_html.md