From 972772451cde7f8aa4b797363634ed559f3c8a5f Mon Sep 17 00:00:00 2001 From: Frank Kunz Date: Sat, 28 Oct 2023 17:23:03 +0200 Subject: [PATCH] Adapt meson build dependencies to support legacy libpodofo Keep old pdf export implementation for libpodofo up to version 0.9.8. Meson build tries to detect the correct podofo version. With 0.10.0 it is possible to use pkgconfig. Signed-off-by: Frank Kunz --- meson.build | 34 ++- src/export_pdf/legacy/canvas_pdf.cpp | 309 +++++++++++++++++++++ src/export_pdf/legacy/canvas_pdf.hpp | 41 +++ src/export_pdf/legacy/export_pdf.cpp | 248 +++++++++++++++++ src/export_pdf/legacy/export_pdf.hpp | 8 + src/export_pdf/legacy/export_pdf_board.cpp | 94 +++++++ src/export_pdf/legacy/export_pdf_board.hpp | 7 + src/export_pdf/legacy/export_pdf_util.cpp | 54 ++++ src/export_pdf/legacy/export_pdf_util.hpp | 8 + 9 files changed, 795 insertions(+), 8 deletions(-) create mode 100644 src/export_pdf/legacy/canvas_pdf.cpp create mode 100644 src/export_pdf/legacy/canvas_pdf.hpp create mode 100644 src/export_pdf/legacy/export_pdf.cpp create mode 100644 src/export_pdf/legacy/export_pdf.hpp create mode 100644 src/export_pdf/legacy/export_pdf_board.cpp create mode 100644 src/export_pdf/legacy/export_pdf_board.hpp create mode 100644 src/export_pdf/legacy/export_pdf_util.cpp create mode 100644 src/export_pdf/legacy/export_pdf_util.hpp diff --git a/meson.build b/meson.build index e567118fe..00c2993d5 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', diff --git a/src/export_pdf/legacy/canvas_pdf.cpp b/src/export_pdf/legacy/canvas_pdf.cpp new file mode 100644 index 000000000..5221986f1 --- /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 000000000..ff3013087 --- /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 000000000..cf8514fac --- /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 000000000..1d8fc99d5 --- /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 000000000..cc1a00628 --- /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 000000000..366ef53d1 --- /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 000000000..09687ed20 --- /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 000000000..a87580eff --- /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()); +}