diff --git a/.github/workflows/all.yml b/.github/workflows/all.yml
index 8294ed86..197098d3 100644
--- a/.github/workflows/all.yml
+++ b/.github/workflows/all.yml
@@ -118,7 +118,7 @@ jobs:
- name: Install updates
run: pacman -Syu --noconfirm
- name: Install dependencies
- run: pacman -Sy --needed --noconfirm zeromq cppzmq gtkmm3 cairomm librsvg sqlite3 libgit2 curl opencascade glm podofo-0.9 libarchive python libspnav cmake meson
+ run: pacman -Sy --needed --noconfirm zeromq cppzmq gtkmm3 cairomm librsvg sqlite3 libgit2 curl opencascade glm podofo libarchive python libspnav cmake meson
- name: Install python dependencies
if: ${{ matrix.target == 'horizon.so' }}
run: pacman -S --needed --noconfirm mesa python-cairo python-yaml
@@ -145,7 +145,6 @@ jobs:
runs-on: ubuntu-latest
container: opensuse/tumbleweed
needs: stylecheck
- continue-on-error: true
steps:
- uses: actions/checkout@v3
- name: Install dependencies
diff --git a/3rd_party/DejaVuSans.ttf b/3rd_party/DejaVuSans.ttf
new file mode 100644
index 00000000..9fe4771d
Binary files /dev/null and b/3rd_party/DejaVuSans.ttf differ
diff --git a/imp.gresource.xml b/imp.gresource.xml
index 2a7730af..d2e42b86 100644
--- a/imp.gresource.xml
+++ b/imp.gresource.xml
@@ -184,5 +184,7 @@
canvas3d/shaders/background-fragment.glsl
color_presets.json
+
+ DejaVuSans.ttf
diff --git a/meson.build b/meson.build
index e567118f..16da87c5 100644
--- a/meson.build
+++ b/meson.build
@@ -65,22 +65,32 @@ if get_option('debug')
cpp_args += '-DCONNECTION_CHECK'
endif
-# pkg-config is useless for podofo :(
+# pkg-config is useless for old podofo :(
+podofo_legacy = true
podofo_lib =cxx.find_library('podofo', dirs: '/usr/lib/podofo-0.9/', required: false, has_headers:['/usr/include/podofo-0.9/podofo/podofo.h'] )
if podofo_lib.found()
-podofo = declare_dependency (
+ podofo = declare_dependency (
dependencies: podofo_lib,
-include_directories: include_directories('/usr/include/podofo-0.9')
-)
+ include_directories: include_directories('/usr/include/podofo-0.9')
+ )
else
podofo = dependency('libpodofo09', required:false)
if podofo.found()
cpp_args += '-DINC_PODOFO_WITHOUT_DIRECTORY'
else
podofo = dependency('libpodofo')
+ if podofo.version() == 'unknown' or podofo.version() < '0.10.0'
+ podofo_legacy = true
+ else
+ podofo_legacy = false
+ endif
endif
endif
+if podofo_legacy
+ message('using legacy podofo')
+endif
+
stdlibs = []
is_libstdcpp = cxx.get_define('__GLIBCXX__', prefix: '#include ') != ''
if is_libstdcpp
@@ -666,10 +676,6 @@ src_export = files(
'src/export_util/tree_writer.cpp',
'src/export_util/tree_writer_fs.cpp',
'src/export_util/tree_writer_archive.cpp',
- 'src/export_pdf/canvas_pdf.cpp',
- 'src/export_pdf/export_pdf.cpp',
- 'src/export_pdf/export_pdf_board.cpp',
- 'src/export_pdf/export_pdf_util.cpp',
'src/export_pnp/export_pnp.cpp',
'src/export_bom/export_bom.cpp',
'src/export_odb/odb_export.cpp',
@@ -686,6 +692,18 @@ src_export = files(
'src/export_odb/structured_text_writer.cpp'
)
+if podofo_legacy
+ src_export += 'src/export_pdf/legacy/canvas_pdf.cpp'
+ src_export += 'src/export_pdf/legacy/export_pdf.cpp'
+ src_export += 'src/export_pdf/legacy/export_pdf_board.cpp'
+ src_export += 'src/export_pdf/legacy/export_pdf_util.cpp'
+else
+ src_export += 'src/export_pdf/canvas_pdf.cpp'
+ src_export += 'src/export_pdf/export_pdf.cpp'
+ src_export += 'src/export_pdf/export_pdf_board.cpp'
+ src_export += 'src/export_pdf/export_pdf_util.cpp'
+endif
+
src_board_rules_check = files(
'src/rules/cache.cpp',
'src/board/board_rules_check.cpp',
@@ -918,7 +936,7 @@ resources = gnome.compile_resources(
'imp.gresource.xml',
dependencies: color_presets_json,
c_name: 'horizon_resources',
- source_dir: 'src'
+ source_dir: ['src', '3rd_party']
)
libhorizon = static_library('libhorizon',
diff --git a/src/export_pdf/canvas_pdf.cpp b/src/export_pdf/canvas_pdf.cpp
index 5221986f..c0d15efe 100644
--- a/src/export_pdf/canvas_pdf.cpp
+++ b/src/export_pdf/canvas_pdf.cpp
@@ -6,6 +6,7 @@
#include "common/hole.hpp"
#include "canvas/appearance.hpp"
#include "board/plane.hpp"
+#include
namespace horizon {
@@ -15,11 +16,12 @@ double to_pt(double x_nm)
}
CanvasPDF::CanvasPDF(PoDoFo::PdfPainter &p, PoDoFo::PdfFont &f, const PDFExportSettings &s)
- : Canvas::Canvas(), painter(p), font(f), settings(s), metrics(font.GetFontMetrics())
+ : Canvas::Canvas(), painter(p), font(f), settings(s), metrics(font.GetMetrics())
{
img_mode = true;
Appearance apperarance;
layer_colors = apperarance.layer_colors;
+ path.Reset();
}
bool CanvasPDF::pdf_layer_visible(int l) const
@@ -42,9 +44,9 @@ void CanvasPDF::img_line(const Coordi &p0, const Coordi &p1, const uint64_t widt
{
if (!pdf_layer_visible(layer))
return;
- painter.Save();
- auto w = std::max(width, settings.min_line_width);
- painter.SetStrokeWidth(to_pt(w));
+
+ auto w = std::max(width, std::max(settings.min_line_width, (uint64_t).001_mm));
+ painter.GraphicsState.SetLineWidth(to_pt(w));
Coordi rp0 = p0;
Coordi rp1 = p1;
if (tr) {
@@ -52,9 +54,8 @@ void CanvasPDF::img_line(const Coordi &p0, const Coordi &p1, const uint64_t widt
rp1 = transform.transform(p1);
}
auto color = get_pdf_layer_color(layer);
- painter.SetStrokingColor(color.r, color.g, color.b);
+ painter.GraphicsState.SetStrokeColor(PoDoFo::PdfColor(color.r, color.g, color.b));
painter.DrawLine(to_pt(rp0.x), to_pt(rp0.y), to_pt(rp1.x), to_pt(rp1.y));
- painter.Restore();
}
void CanvasPDF::img_draw_text(const Coordf &p, float size, const std::string &rtext, int angle, bool flip,
@@ -87,10 +88,11 @@ void CanvasPDF::img_draw_text(const Coordf &p, float size, const std::string &rt
if (mirror) {
lineskip *= -1;
}
- font.SetFontSize(to_pt(size) * 1.6);
+
+ painter.TextState.SetFont(font, to_pt(size) * 1.6);
while (std::getline(ss, line, '\n')) {
line = TextData::trim(line);
- int64_t line_width = metrics->StringWidthMM(line.c_str()) * 1000;
+ int64_t line_width = font.GetStringLength(line.c_str(), painter.TextState);
Placement tf;
tf.shift.x = p.x;
@@ -128,9 +130,9 @@ void CanvasPDF::img_draw_text(const Coordf &p, float size, const std::string &rt
Coordi p0(xshift, yshift);
Coordi pt = tf.transform(p0);
- painter.SetTransformationMatrix(cos(fangle), sin(fangle), -sin(fangle), cos(fangle), to_pt(pt.x), to_pt(pt.y));
- PoDoFo::PdfString pstr(reinterpret_cast(line.c_str()));
- painter.DrawText(0, to_pt(size) / -2, pstr);
+ painter.GraphicsState.SetCurrentMatrix(PoDoFo::Matrix::FromCoefficients(cos(fangle), sin(fangle), -sin(fangle),
+ cos(fangle), to_pt(pt.x), to_pt(pt.y)));
+ painter.DrawText(line.c_str(), 0, to_pt(size) / -2);
painter.Restore();
i_line++;
@@ -139,55 +141,55 @@ void CanvasPDF::img_draw_text(const Coordf &p, float size, const std::string &rt
void CanvasPDF::img_polygon(const Polygon &ipoly, bool tr)
{
+
if (!pdf_layer_visible(ipoly.layer))
return;
- painter.Save();
+
auto color = get_pdf_layer_color(ipoly.layer);
- painter.SetColor(color.r, color.g, color.b);
- painter.SetStrokingColor(color.r, color.g, color.b);
- painter.SetStrokeWidth(to_pt(settings.min_line_width));
+ painter.GraphicsState.SetFillColor(PoDoFo::PdfColor(color.r, color.g, color.b));
+ painter.GraphicsState.SetStrokeColor(PoDoFo::PdfColor(color.r, color.g, color.b));
+ painter.GraphicsState.SetLineWidth(to_pt(settings.min_line_width));
if (ipoly.usage == nullptr) { // regular patch
draw_polygon(ipoly, tr);
if (fill)
- painter.Fill();
+ painter.DrawPath(path, PoDoFo::PdfPathDrawMode::Fill);
else
- painter.Stroke();
+ painter.DrawPath(path, PoDoFo::PdfPathDrawMode::Stroke);
}
else if (auto plane = dynamic_cast(ipoly.usage.ptr)) {
for (const auto &frag : plane->fragments) {
- for (const auto &path : frag.paths) {
+ for (const auto &dpath : frag.paths) {
bool first = true;
- for (const auto &it : path) {
+ for (const auto &it : dpath) {
Coordi p(it.X, it.Y);
if (tr)
p = transform.transform(p);
if (first)
- painter.MoveTo(to_pt(p.x), to_pt(p.y));
+ path.MoveTo(to_pt(p.x), to_pt(p.y));
else
- painter.LineTo(to_pt(p.x), to_pt(p.y));
+ path.AddLineTo(to_pt(p.x), to_pt(p.y));
first = false;
}
- painter.ClosePath();
+ path.Close();
}
}
if (fill)
- painter.Fill(true);
+ painter.DrawPath(path, PoDoFo::PdfPathDrawMode::Fill);
else
- painter.Stroke();
+ painter.DrawPath(path, PoDoFo::PdfPathDrawMode::Stroke);
}
- painter.Restore();
+ path.Reset();
}
void CanvasPDF::img_hole(const Hole &hole)
{
if (!pdf_layer_visible(PDFExportSettings::HOLES_LAYER))
return;
- painter.Save();
auto color = get_pdf_layer_color(PDFExportSettings::HOLES_LAYER);
- painter.SetColor(color.r, color.g, color.b);
- painter.SetStrokingColor(color.r, color.g, color.b);
- painter.SetStrokeWidth(to_pt(settings.min_line_width));
+ painter.GraphicsState.SetFillColor(PoDoFo::PdfColor(color.r, color.g, color.b));
+ painter.GraphicsState.SetStrokeColor(PoDoFo::PdfColor(color.r, color.g, color.b));
+ painter.GraphicsState.SetLineWidth(to_pt(settings.min_line_width));
auto hole2 = hole;
if (settings.set_holes_size) {
@@ -195,10 +197,10 @@ void CanvasPDF::img_hole(const Hole &hole)
}
draw_polygon(hole2.to_polygon(), true);
if (fill)
- painter.Fill(true);
+ painter.DrawPath(path, PoDoFo::PdfPathDrawMode::Fill);
else
- painter.Stroke();
- painter.Restore();
+ painter.DrawPath(path, PoDoFo::PdfPathDrawMode::Stroke);
+ path.Reset();
}
// c is the arc center.
@@ -206,7 +208,7 @@ void CanvasPDF::img_hole(const Hole &hole)
// See "How to determine the control points of a Bézier curve that approximates a
// small circular arc" by Richard ADeVeneza, Nov 2004
// https://www.tinaja.com/glib/bezcirc2.pdf
-static Coordd pdf_arc_segment(PoDoFo::PdfPainter &painter, const Coordd c, const double r, double a0, double a1)
+static Coordd pdf_arc_segment(PoDoFo::PdfPainterPath &path, const Coordd c, const double r, double a0, double a1)
{
const auto da = a0 - a1;
assert(da != 0);
@@ -227,11 +229,11 @@ static Coordd pdf_arc_segment(PoDoFo::PdfPainter &painter, const Coordd c, const
const auto c2 = p2.rotate(theta) * r + c;
const auto c3 = p3.rotate(theta) * r + c;
- painter.CubicBezierTo(to_pt(c1.x), to_pt(c1.y), to_pt(c2.x), to_pt(c2.y), to_pt(c3.x), to_pt(c3.y));
+ path.AddCubicBezierTo(to_pt(c1.x), to_pt(c1.y), to_pt(c2.x), to_pt(c2.y), to_pt(c3.x), to_pt(c3.y));
return c3; // end point
}
-static void pdf_arc(PoDoFo::PdfPainter &painter, const Coordd start, const Coordd c, const Coordd end, bool cw)
+static void pdf_arc(PoDoFo::PdfPainterPath &path, const Coordd start, const Coordd c, const Coordd end, bool cw)
{
const auto r = (start - c).mag();
@@ -258,7 +260,7 @@ static void pdf_arc(PoDoFo::PdfPainter &painter, const Coordd start, const Coord
while (std::abs(e) > 1e-6) {
const auto d = (cw) ? std::max(e, da) : std::min(e, da);
const auto a = a0 + d;
- pdf_arc_segment(painter, c, r, a0, a);
+ pdf_arc_segment(path, c, r, a0, a);
a0 = a;
e = a1 - a0;
}
@@ -278,29 +280,29 @@ void CanvasPDF::draw_polygon(const Polygon &ipoly, bool tr)
it_next = ipoly.vertices.cbegin();
}
if (first) {
- painter.MoveTo(to_pt(p.x), to_pt(p.y));
+ path.MoveTo(to_pt(p.x), to_pt(p.y));
}
if (it->type == Polygon::Vertex::Type::LINE) {
if (!first) {
- painter.LineTo(to_pt(p.x), to_pt(p.y));
+ path.AddLineTo(to_pt(p.x), to_pt(p.y));
}
}
else if (it->type == Polygon::Vertex::Type::ARC) {
Coordd end = it_next->position;
Coordd c = project_onto_perp_bisector(end, it->position, it->arc_center);
if (!first)
- painter.LineTo(to_pt(p.x), to_pt(p.y));
+ path.AddLineTo(to_pt(p.x), to_pt(p.y));
if (tr) {
c = transform.transform(c);
end = transform.transform(end);
}
- pdf_arc(painter, p, c, end, it->arc_reverse);
+ pdf_arc(path, p, c, end, it->arc_reverse);
}
first = false;
}
- painter.ClosePath();
+ path.Close();
}
void CanvasPDF::request_push()
diff --git a/src/export_pdf/canvas_pdf.hpp b/src/export_pdf/canvas_pdf.hpp
index ff301308..2bd143db 100644
--- a/src/export_pdf/canvas_pdf.hpp
+++ b/src/export_pdf/canvas_pdf.hpp
@@ -27,7 +27,7 @@ class CanvasPDF : public Canvas {
PoDoFo::PdfPainter &painter;
PoDoFo::PdfFont &font;
const PDFExportSettings &settings;
- const PoDoFo::PdfFontMetrics *metrics;
+ const PoDoFo::PdfFontMetrics &metrics;
void img_line(const Coordi &p0, const Coordi &p1, const uint64_t width, int layer, bool tr) override;
void img_polygon(const class Polygon &poly, bool tr) override;
void img_draw_text(const Coordf &p, float size, const std::string &rtext, int angle, bool flip, TextOrigin origin,
@@ -37,5 +37,6 @@ class CanvasPDF : public Canvas {
bool pdf_layer_visible(int l) const;
void draw_polygon(const Polygon &ipoly, bool tr);
Color get_pdf_layer_color(int layer) const;
+ PoDoFo::PdfPainterPath path;
};
} // namespace horizon
diff --git a/src/export_pdf/export_pdf.cpp b/src/export_pdf/export_pdf.cpp
index cf8514fa..aa361bba 100644
--- a/src/export_pdf/export_pdf.cpp
+++ b/src/export_pdf/export_pdf.cpp
@@ -7,6 +7,7 @@
#include "schematic/iinstancce_mapping_provider.hpp"
#include "util/bbox_accumulator.hpp"
#include "pool/part.hpp"
+#include
namespace horizon {
@@ -53,17 +54,13 @@ class MyInstanceMappingProvider : public IInstanceMappingProvider {
UUIDVec instance_path;
};
-#if PODOFO_VERSION_MAJOR != 0 || PODOFO_VERSION_MINOR != 9 || PODOFO_VERSION_PATCH != 5
-#define HAVE_OUTLINE
-#endif
-
using Callback = std::function;
class PDFExporter {
public:
PDFExporter(const class PDFExportSettings &settings, Callback callback)
- : document(settings.output_filename.c_str()), font(document.CreateFont("Helvetica")),
- canvas(painter, *font, settings), cb(callback)
+ : document(), font(load_font()), canvas(painter, font, settings), cb(callback),
+ filename(settings.output_filename.c_str())
{
canvas.use_layer_colors = false;
}
@@ -71,55 +68,62 @@ class PDFExporter {
void export_pdf(const class Schematic &sch)
{
cb("Initializing", 0);
- auto info = document.GetInfo();
- info->SetCreator("Horizon EDA");
- info->SetProducer("Horizon EDA");
+ document.GetMetadata().SetCreator(PoDoFo::PdfString("Horizon EDA"));
+ document.GetMetadata().SetProducer(PoDoFo::PdfString("Horizon EDA"));
if (sch.block->project_meta.count("author")) {
- info->SetAuthor(sch.block->project_meta.at("author"));
+ document.GetMetadata().SetAuthor(PoDoFo::PdfString(sch.block->project_meta.at("author")));
}
std::string title = "Schematic";
if (sch.block->project_meta.count("project_title")) {
title = sch.block->project_meta.at("project_title");
}
- info->SetTitle(title);
+ document.GetMetadata().SetTitle(PoDoFo::PdfString(title));
MyInstanceMappingProvider prv(sch);
-#ifdef HAVE_OUTLINE
- outlines = document.GetOutlines();
- PoDoFo::PdfOutlineItem *proot = nullptr;
-#else
- PoDoFo::PdfOutlineItem *proot = nullptr;
-#endif
+ outlines = &document.GetOrCreateOutlines();
+
+ export_schematic(sch, {}, prv, nullptr);
- export_schematic(sch, {}, prv, proot);
for (auto &[path, number, rect] : annotations) {
- auto page = document.GetPage(number);
- auto annot = page->CreateAnnotation(PoDoFo::ePdfAnnotation_Link, rect);
- annot->SetBorderStyle(0, 0, 0);
- annot->SetDestination(first_pages.at(path));
+ auto &page = document.GetPages().GetPageAt(number);
+ auto &annot = page.GetAnnotations().CreateAnnot(rect);
+ annot.SetBorderStyle(0, 0, 0);
+ annot.SetDestination(first_pages.at(path));
}
for (auto &[url, number, rect] : datasheet_annotations) {
- auto page = document.GetPage(number);
- auto annot = page->CreateAnnotation(PoDoFo::ePdfAnnotation_Link, rect);
- annot->SetBorderStyle(0, 0, 0);
+ auto &page = document.GetPages().GetPageAt(number);
+ auto &annot = page.GetAnnotations().CreateAnnot(rect);
+ annot.SetBorderStyle(0, 0, 0);
- PoDoFo::PdfAction action(PoDoFo::ePdfAction_URI, &document);
- action.SetURI(PoDoFo::PdfString(url));
- annot->SetAction(action);
+ auto action = std::make_shared(document, PoDoFo::PdfActionType::URI);
+
+ action->SetURI(PoDoFo::PdfString(url));
+ annot.SetAction(action);
}
- document.Close();
+
+ document.Save(filename);
}
private:
- PoDoFo::PdfStreamedDocument document;
+ PoDoFo::PdfMemDocument document;
PoDoFo::PdfPainter painter;
- PoDoFo::PdfFont *font = nullptr;
- std::map first_pages;
- std::vector> annotations;
- std::vector> datasheet_annotations;
+ PoDoFo::PdfFont &load_font()
+ {
+ auto bytes = Gio::Resource::lookup_data_global("/org/horizon-eda/horizon/DejaVuSans.ttf");
+ gsize size;
+ auto data = bytes->get_data(size);
+ PoDoFo::bufferview buffer{reinterpret_cast(data), size};
+ return document.GetFonts().GetOrCreateFontFromBuffer(buffer);
+ }
+ PoDoFo::PdfFont &font;
PoDoFo::PdfOutlines *outlines = nullptr;
+ std::map> first_pages;
+ std::vector> annotations;
+ std::vector> datasheet_annotations;
+
CanvasPDF canvas;
Callback cb;
+ std::basic_string_view filename;
void export_schematic(const Schematic &sch, const UUIDVec &path, MyInstanceMappingProvider &prv,
PoDoFo::PdfOutlineItem *parent)
@@ -129,24 +133,28 @@ class PDFExporter {
prv.set_instance_path(path);
Schematic my_sch = sch;
my_sch.expand(false, &prv);
- bool first = true;
auto sheets = my_sch.get_sheets_sorted();
+ bool first = true;
+
for (const auto sheet : sheets) {
+
const auto idx = prv.get_sheet_index_for_path(sheet->uuid, path);
const auto progress = (double)idx / prv.get_sheet_total();
cb("Exporting sheet " + format_m_of_n(idx, prv.get_sheet_total()), progress);
- auto page =
- document.CreatePage(PoDoFo::PdfRect(0, 0, to_pt(sheet->frame.width), to_pt(sheet->frame.height)));
- painter.SetPage(page);
- painter.SetLineCapStyle(PoDoFo::ePdfLineCapStyle_Round);
- painter.SetFont(font);
- painter.SetColor(0, 0, 0);
- painter.SetTextRenderingMode(PoDoFo::ePdfTextRenderingMode_Invisible);
+ auto &page = document.GetPages().CreatePage(
+ PoDoFo::Rect(0, 0, to_pt(sheet->frame.width), to_pt(sheet->frame.height)));
+ painter.SetCanvas(page);
+
+ painter.GraphicsState.SetLineCapStyle(PoDoFo::PdfLineCapStyle::Round);
+ painter.GraphicsState.SetFillColor(PoDoFo::PdfColor(0, 0, 0));
+ painter.TextState.SetFont(font, 10);
+ painter.TextState.SetRenderingMode(PoDoFo::PdfTextRenderingMode::Invisible);
for (const auto &[uu, pic] : sheet->pictures) {
if (!pic.on_top)
render_picture(document, painter, pic);
}
+
for (const auto &[uu_sym, sym] : sheet->block_symbols) {
for (const auto &[uu, pic] : sym.symbol.pictures) {
if (!pic.on_top)
@@ -167,7 +175,7 @@ class PDFExporter {
}
}
- auto dest = PoDoFo::PdfDestination(page);
+ auto dest = std::make_shared(page);
if (first) {
first_pages.emplace(path, dest);
first = false;
@@ -187,10 +195,10 @@ class PDFExporter {
acc.accumulate(c);
}
const auto [a, b] = acc.get();
- PoDoFo::PdfRect rect(to_pt(a.x), to_pt(a.y), to_pt(b.x - a.x), to_pt(b.y - a.y));
+ PoDoFo::Rect rect(to_pt(a.x), to_pt(a.y), to_pt(b.x - a.x), to_pt(b.y - a.y));
annotations.emplace_back(
uuid_vec_append(path, sheet->block_symbols.at(ir.uuid).block_instance->uuid),
- page->GetPageNumber() - 1, rect);
+ page.GetPageNumber() - 1, rect);
}
}
else if (ir.type == ObjectType::SCHEMATIC_SYMBOL) {
@@ -203,35 +211,29 @@ class PDFExporter {
acc.accumulate(c);
}
const auto [a, b] = acc.get();
- PoDoFo::PdfRect rect(to_pt(a.x), to_pt(a.y), to_pt(b.x - a.x), to_pt(b.y - a.y));
+ PoDoFo::Rect rect(to_pt(a.x), to_pt(a.y), to_pt(b.x - a.x), to_pt(b.y - a.y));
datasheet_annotations.emplace_back(sym.component->part->get_datasheet(),
- page->GetPageNumber() - 1, rect);
+ page.GetPageNumber() - 1, rect);
}
}
}
}
}
- painter.FinishPage();
+ painter.FinishDrawing();
-#ifdef HAVE_OUTLINE
PoDoFo::PdfOutlineItem *sheet_node;
if (parent) {
- sheet_node = parent->CreateChild(sheet->name, dest);
+ sheet_node = parent->CreateChild(PoDoFo::PdfString(sheet->name), dest);
}
else {
- sheet_node = outlines->CreateRoot(sheet->name);
+ sheet_node = outlines->CreateRoot(PoDoFo::PdfString(sheet->name));
sheet_node->SetDestination(dest);
}
-#endif
for (auto sym : sheet->get_block_symbols_sorted()) {
-#ifdef HAVE_OUTLINE
- auto sym_node = sheet_node->CreateChild(sym->block_instance->refdes, dest);
- sym_node->SetTextFormat(PoDoFo::ePdfOutlineFormat_Italic);
-#else
- PoDoFo::PdfOutlineItem *sym_node = nullptr;
-#endif
+ auto sym_node = sheet_node->CreateChild(PoDoFo::PdfString(sym->block_instance->refdes), dest);
+ sym_node->SetTextFormat(PoDoFo::PdfOutlineFormat::Italic);
export_schematic(*sym->schematic, uuid_vec_append(path, sym->block_instance->uuid), prv, sym_node);
}
}
diff --git a/src/export_pdf/export_pdf_board.cpp b/src/export_pdf/export_pdf_board.cpp
index cc1a0062..d7c8ea0d 100644
--- a/src/export_pdf/export_pdf_board.cpp
+++ b/src/export_pdf/export_pdf_board.cpp
@@ -18,15 +18,13 @@ void export_pdf(const class Board &brd, const class PDFExportSettings &settings,
cb = &cb_nop;
cb("Initializing", 0);
- PoDoFo::PdfStreamedDocument document(settings.output_filename.c_str());
+ PoDoFo::PdfMemDocument document;
PoDoFo::PdfPainter painter;
painter.SetPrecision(9);
- auto info = document.GetInfo();
- info->SetCreator("horizon EDA");
- info->SetProducer("horizon EDA");
+ document.GetMetadata().SetCreator(PoDoFo::PdfString("Horizon EDA"));
+ document.GetMetadata().SetProducer(PoDoFo::PdfString("Horizon EDA"));
-
- auto font = document.CreateFont("Helvetica");
+ auto font = document.GetFonts().SearchFont("Helvetica");
PDFExportSettings my_settings(settings);
my_settings.include_text = false; // need to work out text placement
@@ -38,19 +36,19 @@ void export_pdf(const class Board &brd, const class PDFExportSettings &settings,
auto width = bbox.second.x - bbox.first.x + border_width * 2;
auto height = bbox.second.y - bbox.first.y + border_width * 2;
- auto page = document.CreatePage(PoDoFo::PdfRect(0, 0, to_pt(width), to_pt(height)));
- painter.SetPage(page);
- painter.SetLineCapStyle(PoDoFo::ePdfLineCapStyle_Round);
- painter.SetFont(font);
- painter.SetColor(0, 0, 0);
- painter.SetTextRenderingMode(PoDoFo::ePdfTextRenderingMode_Invisible);
+ auto &page = document.GetPages().CreatePage(PoDoFo::Rect(0, 0, to_pt(width), to_pt(height)));
+ painter.SetCanvas(page);
+ painter.GraphicsState.SetLineCapStyle(PoDoFo::PdfLineCapStyle::Round);
+ painter.GraphicsState.SetFillColor(PoDoFo::PdfColor(0, 0, 0));
+ painter.TextState.SetFont(*font, 10);
+ painter.TextState.SetRenderingMode(PoDoFo::PdfTextRenderingMode::Invisible);
if (settings.mirror) {
- painter.SetTransformationMatrix(-1, 0, 0, 1, to_pt(bbox.second.x + border_width),
- to_pt(-bbox.first.y + border_width));
+ painter.GraphicsState.SetCurrentMatrix(PoDoFo::Matrix::FromCoefficients(
+ -1, 0, 0, 1, to_pt(bbox.second.x + border_width), to_pt(-bbox.first.y + border_width)));
}
else {
- painter.SetTransformationMatrix(1, 0, 0, 1, to_pt(-bbox.first.x + border_width),
- to_pt(-bbox.first.y + border_width));
+ painter.GraphicsState.SetCurrentMatrix(PoDoFo::Matrix::FromCoefficients(
+ 1, 0, 0, 1, to_pt(-bbox.first.x + border_width), to_pt(-bbox.first.y + border_width)));
}
ca.layer_filter = true;
ca.use_layer_colors = true;
@@ -87,8 +85,8 @@ void export_pdf(const class Board &brd, const class PDFExportSettings &settings,
render_picture(document, painter, pic);
}
- painter.FinishPage();
+ painter.FinishDrawing();
- document.Close();
+ document.Save(settings.output_filename.c_str());
}
} // namespace horizon
diff --git a/src/export_pdf/export_pdf_util.cpp b/src/export_pdf/export_pdf_util.cpp
index 09687ed2..50aeeaf4 100644
--- a/src/export_pdf/export_pdf_util.cpp
+++ b/src/export_pdf/export_pdf_util.cpp
@@ -4,51 +4,23 @@
namespace horizon {
+#define CONVERSION_CONSTANT 0.002834645669291339
+
void render_picture(PoDoFo::PdfDocument &doc, PoDoFo::PdfPainter &painter, const Picture &pic, const Placement &tr)
{
- PoDoFo::PdfImage img(&doc);
+ auto img = doc.CreateImage();
Placement pl = tr;
pl.accumulate(pic.placement);
- {
- std::vector picdata;
- picdata.reserve(pic.data->width * pic.data->height * 3);
- for (const auto x : pic.data->data) {
- picdata.push_back((x)&0xff);
- picdata.push_back((x >> 8) & 0xff);
- picdata.push_back((x >> 16) & 0xff);
- }
-
- PoDoFo::PdfMemoryInputStream stream(picdata.data(), picdata.size());
- img.SetImageColorSpace(PoDoFo::ePdfColorSpace_DeviceRGB);
- img.SetImageData(pic.data->width, pic.data->height, 8, &stream);
- }
-
- PoDoFo::PdfImage img_mask(&doc);
- {
- std::vector picdata;
- picdata.reserve(pic.data->width * pic.data->height);
- for (const auto x : pic.data->data) {
- picdata.push_back(((x >> 24) & 0xff) * pic.opacity);
- }
-
- PoDoFo::PdfMemoryInputStream stream(picdata.data(), picdata.size());
- img_mask.SetImageColorSpace(PoDoFo::ePdfColorSpace_DeviceGray);
- img_mask.SetImageData(pic.data->width, pic.data->height, 8, &stream);
- }
-
- img.SetImageSoftmask(&img_mask);
-
painter.Save();
const auto fangle = pl.get_angle_rad();
-
- painter.SetTransformationMatrix(cos(fangle), sin(fangle), -sin(fangle), cos(fangle), to_pt((double)pl.shift.x),
- to_pt((double)pl.shift.y));
+ painter.GraphicsState.SetCurrentMatrix(PoDoFo::Matrix::FromCoefficients(
+ cos(fangle), sin(fangle), -sin(fangle), cos(fangle), to_pt((double)pl.shift.x), to_pt((double)pl.shift.y)));
const int64_t w = pic.data->width * pic.px_size;
const int64_t h = pic.data->height * pic.px_size;
const auto p = Coordd(w, h) / -2;
const double sz = pic.px_size / (1e3 / CONVERSION_CONSTANT);
- painter.DrawImage(to_pt(p.x), to_pt(p.y), &img, sz, sz);
+ painter.DrawImage(*img, to_pt(p.x), to_pt(p.y), sz, sz);
painter.Restore();
}
} // namespace horizon
diff --git a/src/export_pdf/legacy/canvas_pdf.cpp b/src/export_pdf/legacy/canvas_pdf.cpp
new file mode 100644
index 00000000..5221986f
--- /dev/null
+++ b/src/export_pdf/legacy/canvas_pdf.cpp
@@ -0,0 +1,309 @@
+#include "canvas_pdf.hpp"
+#include "common/pdf_export_settings.hpp"
+#include "util/str_util.hpp"
+#include "util/geom_util.hpp"
+#include "common/polygon.hpp"
+#include "common/hole.hpp"
+#include "canvas/appearance.hpp"
+#include "board/plane.hpp"
+
+namespace horizon {
+
+double to_pt(double x_nm)
+{
+ return x_nm * .000002834645669291339;
+}
+
+CanvasPDF::CanvasPDF(PoDoFo::PdfPainter &p, PoDoFo::PdfFont &f, const PDFExportSettings &s)
+ : Canvas::Canvas(), painter(p), font(f), settings(s), metrics(font.GetFontMetrics())
+{
+ img_mode = true;
+ Appearance apperarance;
+ layer_colors = apperarance.layer_colors;
+}
+
+bool CanvasPDF::pdf_layer_visible(int l) const
+{
+ if (layer_filter == false)
+ return true;
+ else
+ return l == current_layer;
+}
+
+Color CanvasPDF::get_pdf_layer_color(int layer) const
+{
+ if (use_layer_colors)
+ return get_layer_color(layer);
+ else
+ return Color(0, 0, 0);
+}
+
+void CanvasPDF::img_line(const Coordi &p0, const Coordi &p1, const uint64_t width, int layer, bool tr)
+{
+ if (!pdf_layer_visible(layer))
+ return;
+ painter.Save();
+ auto w = std::max(width, settings.min_line_width);
+ painter.SetStrokeWidth(to_pt(w));
+ Coordi rp0 = p0;
+ Coordi rp1 = p1;
+ if (tr) {
+ rp0 = transform.transform(p0);
+ rp1 = transform.transform(p1);
+ }
+ auto color = get_pdf_layer_color(layer);
+ painter.SetStrokingColor(color.r, color.g, color.b);
+ painter.DrawLine(to_pt(rp0.x), to_pt(rp0.y), to_pt(rp1.x), to_pt(rp1.y));
+ painter.Restore();
+}
+
+void CanvasPDF::img_draw_text(const Coordf &p, float size, const std::string &rtext, int angle, bool flip,
+ TextOrigin origin, int layer, uint64_t width, TextData::Font tfont, bool center,
+ bool mirror)
+{
+ if (!settings.include_text)
+ return;
+ if (!pdf_layer_visible(layer))
+ return;
+
+ angle = wrap_angle(angle);
+ bool backwards = (angle > 16384) && (angle <= 49152);
+ float yshift = 0;
+ switch (origin) {
+ case TextOrigin::CENTER:
+ yshift = 0;
+ break;
+ default:
+ yshift = size / 2;
+ }
+
+ std::string text(rtext);
+ trim(text);
+ std::stringstream ss(text);
+ std::string line;
+ unsigned int n_lines = std::count(text.begin(), text.end(), '\n');
+ unsigned int i_line = 0;
+ float lineskip = size * 1.35 + width;
+ if (mirror) {
+ lineskip *= -1;
+ }
+ font.SetFontSize(to_pt(size) * 1.6);
+ while (std::getline(ss, line, '\n')) {
+ line = TextData::trim(line);
+ int64_t line_width = metrics->StringWidthMM(line.c_str()) * 1000;
+
+ Placement tf;
+ tf.shift.x = p.x;
+ tf.shift.y = p.y;
+
+ Placement tr;
+ if (flip)
+ tr.set_angle(32768 - angle);
+ else
+ tr.set_angle(angle);
+ if (backwards ^ mirror)
+ tf.shift += tr.transform(Coordi(0, -lineskip * (n_lines - i_line)));
+ else
+ tf.shift += tr.transform(Coordi(0, -lineskip * i_line));
+
+ int xshift = 0;
+ if (backwards) {
+ tf.set_angle(angle - 32768);
+ xshift = -line_width;
+ }
+ else {
+ tf.set_angle(angle);
+ }
+ tf.mirror = flip;
+ if (center) {
+ if (backwards) {
+ xshift += line_width / 2;
+ }
+ else {
+ xshift -= line_width / 2;
+ }
+ }
+ double fangle = tf.get_angle_rad();
+ painter.Save();
+ Coordi p0(xshift, yshift);
+ Coordi pt = tf.transform(p0);
+
+ painter.SetTransformationMatrix(cos(fangle), sin(fangle), -sin(fangle), cos(fangle), to_pt(pt.x), to_pt(pt.y));
+ PoDoFo::PdfString pstr(reinterpret_cast(line.c_str()));
+ painter.DrawText(0, to_pt(size) / -2, pstr);
+ painter.Restore();
+
+ i_line++;
+ }
+}
+
+void CanvasPDF::img_polygon(const Polygon &ipoly, bool tr)
+{
+ if (!pdf_layer_visible(ipoly.layer))
+ return;
+ painter.Save();
+ auto color = get_pdf_layer_color(ipoly.layer);
+ painter.SetColor(color.r, color.g, color.b);
+ painter.SetStrokingColor(color.r, color.g, color.b);
+ painter.SetStrokeWidth(to_pt(settings.min_line_width));
+ if (ipoly.usage == nullptr) { // regular patch
+ draw_polygon(ipoly, tr);
+ if (fill)
+ painter.Fill();
+ else
+ painter.Stroke();
+ }
+ else if (auto plane = dynamic_cast(ipoly.usage.ptr)) {
+ for (const auto &frag : plane->fragments) {
+ for (const auto &path : frag.paths) {
+ bool first = true;
+ for (const auto &it : path) {
+ Coordi p(it.X, it.Y);
+ if (tr)
+ p = transform.transform(p);
+ if (first)
+ painter.MoveTo(to_pt(p.x), to_pt(p.y));
+ else
+ painter.LineTo(to_pt(p.x), to_pt(p.y));
+ first = false;
+ }
+ painter.ClosePath();
+ }
+ }
+ if (fill)
+ painter.Fill(true);
+ else
+ painter.Stroke();
+ }
+ painter.Restore();
+}
+
+void CanvasPDF::img_hole(const Hole &hole)
+{
+ if (!pdf_layer_visible(PDFExportSettings::HOLES_LAYER))
+ return;
+ painter.Save();
+
+ auto color = get_pdf_layer_color(PDFExportSettings::HOLES_LAYER);
+ painter.SetColor(color.r, color.g, color.b);
+ painter.SetStrokingColor(color.r, color.g, color.b);
+ painter.SetStrokeWidth(to_pt(settings.min_line_width));
+
+ auto hole2 = hole;
+ if (settings.set_holes_size) {
+ hole2.diameter = settings.holes_diameter;
+ }
+ draw_polygon(hole2.to_polygon(), true);
+ if (fill)
+ painter.Fill(true);
+ else
+ painter.Stroke();
+ painter.Restore();
+}
+
+// c is the arc center.
+// angles must be in radians, c and r must be in mm.
+// See "How to determine the control points of a Bézier curve that approximates a
+// small circular arc" by Richard ADeVeneza, Nov 2004
+// https://www.tinaja.com/glib/bezcirc2.pdf
+static Coordd pdf_arc_segment(PoDoFo::PdfPainter &painter, const Coordd c, const double r, double a0, double a1)
+{
+ const auto da = a0 - a1;
+ assert(da != 0);
+ assert(std::abs(da) <= M_PI / 2 + 1e-6);
+
+ // Shift to bisect at x axis
+ const auto theta = (a0 + a1) / 2;
+ const auto phi = da / 2;
+
+ // Compute points of unit circle for given delta angle
+ const auto p0 = Coordd(cos(phi), sin(phi));
+ const auto p1 = Coordd((4 - p0.x) / 3, (1 - p0.x) * (3 - p0.x) / (3 * p0.y));
+ const auto p2 = Coordd(p1.x, -p1.y);
+ const auto p3 = Coordd(p0.x, -p0.y);
+
+ // Transform points
+ const auto c1 = p1.rotate(theta) * r + c;
+ const auto c2 = p2.rotate(theta) * r + c;
+ const auto c3 = p3.rotate(theta) * r + c;
+
+ painter.CubicBezierTo(to_pt(c1.x), to_pt(c1.y), to_pt(c2.x), to_pt(c2.y), to_pt(c3.x), to_pt(c3.y));
+ return c3; // end point
+}
+
+static void pdf_arc(PoDoFo::PdfPainter &painter, const Coordd start, const Coordd c, const Coordd end, bool cw)
+{
+ const auto r = (start - c).mag();
+
+ // Get angles relative to the x axis
+ double a0 = (start - c).angle();
+ double a1 = (end - c).angle();
+
+ // Circle or large arc
+ if (cw && a0 <= a1) {
+ a0 += 2 * M_PI;
+ }
+ else if (!cw && a0 >= a1) {
+ a0 -= 2 * M_PI;
+ }
+
+ const double da = (cw) ? -M_PI / 2 : M_PI / 2;
+ if (cw) {
+ assert(a0 > a1);
+ }
+ else {
+ assert(a0 < a1);
+ }
+ double e = a1 - a0;
+ while (std::abs(e) > 1e-6) {
+ const auto d = (cw) ? std::max(e, da) : std::min(e, da);
+ const auto a = a0 + d;
+ pdf_arc_segment(painter, c, r, a0, a);
+ a0 = a;
+ e = a1 - a0;
+ }
+}
+
+
+void CanvasPDF::draw_polygon(const Polygon &ipoly, bool tr)
+{
+ assert(ipoly.usage == nullptr);
+ bool first = true;
+ for (auto it = ipoly.vertices.cbegin(); it < ipoly.vertices.cend(); it++) {
+ Coordd p = it->position;
+ if (tr)
+ p = transform.transform(p);
+ auto it_next = it + 1;
+ if (it_next == ipoly.vertices.cend()) {
+ it_next = ipoly.vertices.cbegin();
+ }
+ if (first) {
+ painter.MoveTo(to_pt(p.x), to_pt(p.y));
+ }
+ if (it->type == Polygon::Vertex::Type::LINE) {
+ if (!first) {
+ painter.LineTo(to_pt(p.x), to_pt(p.y));
+ }
+ }
+ else if (it->type == Polygon::Vertex::Type::ARC) {
+ Coordd end = it_next->position;
+ Coordd c = project_onto_perp_bisector(end, it->position, it->arc_center);
+ if (!first)
+ painter.LineTo(to_pt(p.x), to_pt(p.y));
+
+ if (tr) {
+ c = transform.transform(c);
+ end = transform.transform(end);
+ }
+ pdf_arc(painter, p, c, end, it->arc_reverse);
+ }
+ first = false;
+ }
+
+ painter.ClosePath();
+}
+
+void CanvasPDF::request_push()
+{
+}
+} // namespace horizon
diff --git a/src/export_pdf/legacy/canvas_pdf.hpp b/src/export_pdf/legacy/canvas_pdf.hpp
new file mode 100644
index 00000000..ff301308
--- /dev/null
+++ b/src/export_pdf/legacy/canvas_pdf.hpp
@@ -0,0 +1,41 @@
+#pragma once
+#include "canvas/canvas.hpp"
+#include "util/podofo_inc.hpp"
+
+namespace horizon {
+
+double to_pt(double x_nm);
+
+class CanvasPDF : public Canvas {
+public:
+ CanvasPDF(PoDoFo::PdfPainter &painter, PoDoFo::PdfFont &font, const class PDFExportSettings &settings);
+ void push() override
+ {
+ }
+
+ void request_push() override;
+ bool layer_filter = false;
+ int current_layer = 0;
+ bool fill = true;
+ bool use_layer_colors = false;
+ const auto &get_selectables() const
+ {
+ return selectables;
+ }
+
+private:
+ PoDoFo::PdfPainter &painter;
+ PoDoFo::PdfFont &font;
+ const PDFExportSettings &settings;
+ const PoDoFo::PdfFontMetrics *metrics;
+ void img_line(const Coordi &p0, const Coordi &p1, const uint64_t width, int layer, bool tr) override;
+ void img_polygon(const class Polygon &poly, bool tr) override;
+ void img_draw_text(const Coordf &p, float size, const std::string &rtext, int angle, bool flip, TextOrigin origin,
+ int layer = 10000, uint64_t width = 0, TextData::Font font = TextData::Font::SIMPLEX,
+ bool center = false, bool mirror = false) override;
+ void img_hole(const Hole &hole) override;
+ bool pdf_layer_visible(int l) const;
+ void draw_polygon(const Polygon &ipoly, bool tr);
+ Color get_pdf_layer_color(int layer) const;
+};
+} // namespace horizon
diff --git a/src/export_pdf/legacy/export_pdf.cpp b/src/export_pdf/legacy/export_pdf.cpp
new file mode 100644
index 00000000..cf8514fa
--- /dev/null
+++ b/src/export_pdf/legacy/export_pdf.cpp
@@ -0,0 +1,248 @@
+#include "export_pdf.hpp"
+#include "canvas_pdf.hpp"
+#include "util/podofo_inc.hpp"
+#include "util/util.hpp"
+#include "schematic/schematic.hpp"
+#include "export_pdf_util.hpp"
+#include "schematic/iinstancce_mapping_provider.hpp"
+#include "util/bbox_accumulator.hpp"
+#include "pool/part.hpp"
+
+namespace horizon {
+
+static void cb_nop(std::string, double)
+{
+}
+
+class MyInstanceMappingProvider : public IInstanceMappingProvider {
+public:
+ MyInstanceMappingProvider(const Schematic &sch) : top(sch)
+ {
+ }
+ void set_instance_path(const UUIDVec &p)
+ {
+ instance_path = p;
+ }
+
+ const class BlockInstanceMapping *get_block_instance_mapping() const override
+ {
+ if (instance_path.size())
+ return &top.block->block_instance_mappings.at(instance_path);
+ else
+ return nullptr;
+ }
+
+ unsigned int get_sheet_index_for_path(const class UUID &sheet, const UUIDVec &path) const
+ {
+ return top.sheet_mapping.sheet_numbers.at(uuid_vec_append(path, sheet));
+ }
+
+ unsigned int get_sheet_index(const class UUID &sheet) const override
+ {
+ return get_sheet_index_for_path(sheet, instance_path);
+ }
+
+ unsigned int get_sheet_total() const override
+ {
+ return top.sheet_mapping.sheet_total;
+ }
+
+
+private:
+ const Schematic ⊤
+ UUIDVec instance_path;
+};
+
+#if PODOFO_VERSION_MAJOR != 0 || PODOFO_VERSION_MINOR != 9 || PODOFO_VERSION_PATCH != 5
+#define HAVE_OUTLINE
+#endif
+
+using Callback = std::function;
+
+class PDFExporter {
+public:
+ PDFExporter(const class PDFExportSettings &settings, Callback callback)
+ : document(settings.output_filename.c_str()), font(document.CreateFont("Helvetica")),
+ canvas(painter, *font, settings), cb(callback)
+ {
+ canvas.use_layer_colors = false;
+ }
+
+ void export_pdf(const class Schematic &sch)
+ {
+ cb("Initializing", 0);
+ auto info = document.GetInfo();
+ info->SetCreator("Horizon EDA");
+ info->SetProducer("Horizon EDA");
+ if (sch.block->project_meta.count("author")) {
+ info->SetAuthor(sch.block->project_meta.at("author"));
+ }
+ std::string title = "Schematic";
+ if (sch.block->project_meta.count("project_title")) {
+ title = sch.block->project_meta.at("project_title");
+ }
+ info->SetTitle(title);
+ MyInstanceMappingProvider prv(sch);
+
+#ifdef HAVE_OUTLINE
+ outlines = document.GetOutlines();
+ PoDoFo::PdfOutlineItem *proot = nullptr;
+#else
+ PoDoFo::PdfOutlineItem *proot = nullptr;
+#endif
+
+ export_schematic(sch, {}, prv, proot);
+ for (auto &[path, number, rect] : annotations) {
+ auto page = document.GetPage(number);
+ auto annot = page->CreateAnnotation(PoDoFo::ePdfAnnotation_Link, rect);
+ annot->SetBorderStyle(0, 0, 0);
+ annot->SetDestination(first_pages.at(path));
+ }
+ for (auto &[url, number, rect] : datasheet_annotations) {
+ auto page = document.GetPage(number);
+ auto annot = page->CreateAnnotation(PoDoFo::ePdfAnnotation_Link, rect);
+ annot->SetBorderStyle(0, 0, 0);
+
+ PoDoFo::PdfAction action(PoDoFo::ePdfAction_URI, &document);
+ action.SetURI(PoDoFo::PdfString(url));
+ annot->SetAction(action);
+ }
+ document.Close();
+ }
+
+private:
+ PoDoFo::PdfStreamedDocument document;
+ PoDoFo::PdfPainter painter;
+ PoDoFo::PdfFont *font = nullptr;
+ std::map first_pages;
+ std::vector> annotations;
+ std::vector> datasheet_annotations;
+ PoDoFo::PdfOutlines *outlines = nullptr;
+ CanvasPDF canvas;
+ Callback cb;
+
+ void export_schematic(const Schematic &sch, const UUIDVec &path, MyInstanceMappingProvider &prv,
+ PoDoFo::PdfOutlineItem *parent)
+ {
+ if (Block::instance_path_too_long(path, __FUNCTION__))
+ return;
+ prv.set_instance_path(path);
+ Schematic my_sch = sch;
+ my_sch.expand(false, &prv);
+ bool first = true;
+ auto sheets = my_sch.get_sheets_sorted();
+ for (const auto sheet : sheets) {
+ const auto idx = prv.get_sheet_index_for_path(sheet->uuid, path);
+ const auto progress = (double)idx / prv.get_sheet_total();
+ cb("Exporting sheet " + format_m_of_n(idx, prv.get_sheet_total()), progress);
+ auto page =
+ document.CreatePage(PoDoFo::PdfRect(0, 0, to_pt(sheet->frame.width), to_pt(sheet->frame.height)));
+ painter.SetPage(page);
+ painter.SetLineCapStyle(PoDoFo::ePdfLineCapStyle_Round);
+ painter.SetFont(font);
+ painter.SetColor(0, 0, 0);
+ painter.SetTextRenderingMode(PoDoFo::ePdfTextRenderingMode_Invisible);
+
+ for (const auto &[uu, pic] : sheet->pictures) {
+ if (!pic.on_top)
+ render_picture(document, painter, pic);
+ }
+ for (const auto &[uu_sym, sym] : sheet->block_symbols) {
+ for (const auto &[uu, pic] : sym.symbol.pictures) {
+ if (!pic.on_top)
+ render_picture(document, painter, pic, sym.placement);
+ }
+ }
+
+ canvas.update(*sheet);
+
+ for (const auto &[uu, pic] : sheet->pictures) {
+ if (pic.on_top)
+ render_picture(document, painter, pic);
+ }
+ for (const auto &[uu_sym, sym] : sheet->block_symbols) {
+ for (const auto &[uu, pic] : sym.symbol.pictures) {
+ if (pic.on_top)
+ render_picture(document, painter, pic, sym.placement);
+ }
+ }
+
+ auto dest = PoDoFo::PdfDestination(page);
+ if (first) {
+ first_pages.emplace(path, dest);
+ first = false;
+ }
+
+ {
+ const auto &items = canvas.get_selectables().get_items();
+ const auto &items_ref = canvas.get_selectables().get_items_ref();
+ const auto n = items.size();
+ for (size_t i = 0; i < n; i++) {
+ const auto &it = items.at(i);
+ const auto &ir = items_ref.at(i);
+ if (ir.type == ObjectType::SCHEMATIC_BLOCK_SYMBOL) {
+ if (it.is_box()) {
+ BBoxAccumulator acc;
+ for (const auto &c : it.get_corners()) {
+ acc.accumulate(c);
+ }
+ const auto [a, b] = acc.get();
+ PoDoFo::PdfRect rect(to_pt(a.x), to_pt(a.y), to_pt(b.x - a.x), to_pt(b.y - a.y));
+ annotations.emplace_back(
+ uuid_vec_append(path, sheet->block_symbols.at(ir.uuid).block_instance->uuid),
+ page->GetPageNumber() - 1, rect);
+ }
+ }
+ else if (ir.type == ObjectType::SCHEMATIC_SYMBOL) {
+ if (it.is_box()) {
+ const auto &sym = sheet->symbols.at(ir.uuid);
+ if (sym.component->part && sym.component->part->get_datasheet().size()) {
+ BBoxAccumulator acc;
+
+ for (const auto &c : it.get_corners()) {
+ acc.accumulate(c);
+ }
+ const auto [a, b] = acc.get();
+ PoDoFo::PdfRect rect(to_pt(a.x), to_pt(a.y), to_pt(b.x - a.x), to_pt(b.y - a.y));
+ datasheet_annotations.emplace_back(sym.component->part->get_datasheet(),
+ page->GetPageNumber() - 1, rect);
+ }
+ }
+ }
+ }
+ }
+
+ painter.FinishPage();
+
+#ifdef HAVE_OUTLINE
+ PoDoFo::PdfOutlineItem *sheet_node;
+ if (parent) {
+ sheet_node = parent->CreateChild(sheet->name, dest);
+ }
+ else {
+ sheet_node = outlines->CreateRoot(sheet->name);
+ sheet_node->SetDestination(dest);
+ }
+#endif
+
+ for (auto sym : sheet->get_block_symbols_sorted()) {
+#ifdef HAVE_OUTLINE
+ auto sym_node = sheet_node->CreateChild(sym->block_instance->refdes, dest);
+ sym_node->SetTextFormat(PoDoFo::ePdfOutlineFormat_Italic);
+#else
+ PoDoFo::PdfOutlineItem *sym_node = nullptr;
+#endif
+ export_schematic(*sym->schematic, uuid_vec_append(path, sym->block_instance->uuid), prv, sym_node);
+ }
+ }
+ }
+};
+
+void export_pdf(const class Schematic &sch, const class PDFExportSettings &settings, Callback cb)
+{
+ if (!cb)
+ cb = &cb_nop;
+ PDFExporter ex(settings, cb);
+ ex.export_pdf(sch);
+}
+} // namespace horizon
diff --git a/src/export_pdf/legacy/export_pdf.hpp b/src/export_pdf/legacy/export_pdf.hpp
new file mode 100644
index 00000000..1d8fc99d
--- /dev/null
+++ b/src/export_pdf/legacy/export_pdf.hpp
@@ -0,0 +1,8 @@
+#pragma once
+#include
+#include
+
+namespace horizon {
+void export_pdf(const class Schematic &sch, const class PDFExportSettings &settings,
+ std::function cb = nullptr);
+}
diff --git a/src/export_pdf/legacy/export_pdf_board.cpp b/src/export_pdf/legacy/export_pdf_board.cpp
new file mode 100644
index 00000000..cc1a0062
--- /dev/null
+++ b/src/export_pdf/legacy/export_pdf_board.cpp
@@ -0,0 +1,94 @@
+#include "export_pdf.hpp"
+#include "canvas_pdf.hpp"
+#include "util/podofo_inc.hpp"
+#include "util/util.hpp"
+#include "board/board.hpp"
+#include "export_pdf_util.hpp"
+
+namespace horizon {
+
+static void cb_nop(std::string, double)
+{
+}
+
+void export_pdf(const class Board &brd, const class PDFExportSettings &settings,
+ std::function cb)
+{
+ if (!cb)
+ cb = &cb_nop;
+ cb("Initializing", 0);
+
+ PoDoFo::PdfStreamedDocument document(settings.output_filename.c_str());
+ PoDoFo::PdfPainter painter;
+ painter.SetPrecision(9);
+ auto info = document.GetInfo();
+ info->SetCreator("horizon EDA");
+ info->SetProducer("horizon EDA");
+
+
+ auto font = document.CreateFont("Helvetica");
+
+ PDFExportSettings my_settings(settings);
+ my_settings.include_text = false; // need to work out text placement
+ CanvasPDF ca(painter, *font, my_settings);
+
+ cb("Exporting Board", 0);
+ int64_t border_width = 1_mm;
+ auto bbox = brd.get_bbox();
+ auto width = bbox.second.x - bbox.first.x + border_width * 2;
+ auto height = bbox.second.y - bbox.first.y + border_width * 2;
+
+ auto page = document.CreatePage(PoDoFo::PdfRect(0, 0, to_pt(width), to_pt(height)));
+ painter.SetPage(page);
+ painter.SetLineCapStyle(PoDoFo::ePdfLineCapStyle_Round);
+ painter.SetFont(font);
+ painter.SetColor(0, 0, 0);
+ painter.SetTextRenderingMode(PoDoFo::ePdfTextRenderingMode_Invisible);
+ if (settings.mirror) {
+ painter.SetTransformationMatrix(-1, 0, 0, 1, to_pt(bbox.second.x + border_width),
+ to_pt(-bbox.first.y + border_width));
+ }
+ else {
+ painter.SetTransformationMatrix(1, 0, 0, 1, to_pt(-bbox.first.x + border_width),
+ to_pt(-bbox.first.y + border_width));
+ }
+ ca.layer_filter = true;
+ ca.use_layer_colors = true;
+
+ std::vector layers_sorted;
+ for (const auto &it : settings.layers) {
+ if (it.second.enabled) {
+ layers_sorted.push_back(it.first);
+ ca.set_layer_color(it.first, it.second.color);
+ }
+ }
+ std::sort(layers_sorted.begin(), layers_sorted.end(),
+ [&brd](const auto a, const auto b) { return brd.get_layer_position(a) < brd.get_layer_position(b); });
+ if (settings.reverse_layers)
+ std::reverse(layers_sorted.begin(), layers_sorted.end());
+
+ for (const auto &[uu, pic] : brd.pictures) {
+ if (!pic.on_top)
+ render_picture(document, painter, pic);
+ }
+
+ unsigned int i_layer = 0;
+ for (int layer : layers_sorted) {
+ ca.clear();
+ ca.current_layer = layer;
+ ca.fill = settings.layers.at(layer).mode == PDFExportSettings::Layer::Mode::FILL;
+ cb("Exporting layer " + format_m_of_n(i_layer, layers_sorted.size()), ((double)i_layer) / layers_sorted.size());
+ ca.update(brd);
+ i_layer++;
+ }
+
+ for (const auto &[uu, pic] : brd.pictures) {
+ if (pic.on_top)
+ render_picture(document, painter, pic);
+ }
+
+ painter.FinishPage();
+
+ document.Close();
+}
+} // namespace horizon
diff --git a/src/export_pdf/legacy/export_pdf_board.hpp b/src/export_pdf/legacy/export_pdf_board.hpp
new file mode 100644
index 00000000..366ef53d
--- /dev/null
+++ b/src/export_pdf/legacy/export_pdf_board.hpp
@@ -0,0 +1,7 @@
+#pragma once
+#include
+
+namespace horizon {
+void export_pdf(const class Board &brd, const class PDFExportSettings &settings,
+ std::function cb = nullptr);
+}
diff --git a/src/export_pdf/legacy/export_pdf_util.cpp b/src/export_pdf/legacy/export_pdf_util.cpp
new file mode 100644
index 00000000..09687ed2
--- /dev/null
+++ b/src/export_pdf/legacy/export_pdf_util.cpp
@@ -0,0 +1,54 @@
+#include "canvas_pdf.hpp"
+#include "common/picture.hpp"
+#include "export_pdf_util.hpp"
+
+namespace horizon {
+
+void render_picture(PoDoFo::PdfDocument &doc, PoDoFo::PdfPainter &painter, const Picture &pic, const Placement &tr)
+{
+ PoDoFo::PdfImage img(&doc);
+ Placement pl = tr;
+ pl.accumulate(pic.placement);
+
+ {
+ std::vector picdata;
+ picdata.reserve(pic.data->width * pic.data->height * 3);
+ for (const auto x : pic.data->data) {
+ picdata.push_back((x)&0xff);
+ picdata.push_back((x >> 8) & 0xff);
+ picdata.push_back((x >> 16) & 0xff);
+ }
+
+ PoDoFo::PdfMemoryInputStream stream(picdata.data(), picdata.size());
+ img.SetImageColorSpace(PoDoFo::ePdfColorSpace_DeviceRGB);
+ img.SetImageData(pic.data->width, pic.data->height, 8, &stream);
+ }
+
+ PoDoFo::PdfImage img_mask(&doc);
+ {
+ std::vector picdata;
+ picdata.reserve(pic.data->width * pic.data->height);
+ for (const auto x : pic.data->data) {
+ picdata.push_back(((x >> 24) & 0xff) * pic.opacity);
+ }
+
+ PoDoFo::PdfMemoryInputStream stream(picdata.data(), picdata.size());
+ img_mask.SetImageColorSpace(PoDoFo::ePdfColorSpace_DeviceGray);
+ img_mask.SetImageData(pic.data->width, pic.data->height, 8, &stream);
+ }
+
+ img.SetImageSoftmask(&img_mask);
+
+ painter.Save();
+ const auto fangle = pl.get_angle_rad();
+
+ painter.SetTransformationMatrix(cos(fangle), sin(fangle), -sin(fangle), cos(fangle), to_pt((double)pl.shift.x),
+ to_pt((double)pl.shift.y));
+ const int64_t w = pic.data->width * pic.px_size;
+ const int64_t h = pic.data->height * pic.px_size;
+ const auto p = Coordd(w, h) / -2;
+ const double sz = pic.px_size / (1e3 / CONVERSION_CONSTANT);
+ painter.DrawImage(to_pt(p.x), to_pt(p.y), &img, sz, sz);
+ painter.Restore();
+}
+} // namespace horizon
diff --git a/src/export_pdf/legacy/export_pdf_util.hpp b/src/export_pdf/legacy/export_pdf_util.hpp
new file mode 100644
index 00000000..a87580ef
--- /dev/null
+++ b/src/export_pdf/legacy/export_pdf_util.hpp
@@ -0,0 +1,8 @@
+#pragma once
+#include "util/podofo_inc.hpp"
+#include "util/placement.hpp"
+
+namespace horizon {
+void render_picture(PoDoFo::PdfDocument &doc, PoDoFo::PdfPainter &painter, const class Picture &pic,
+ const Placement &tr = Placement());
+}