/* KLayout Layout Viewer Copyright (C) 2006-2018 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "dbGerberImporter.h" #include "dbGerberImportData.h" #include "dbGerberDrillFileReader.h" #include "dbRS274XReader.h" #include "dbReader.h" #include "dbStream.h" #include "tlStream.h" #include "tlString.h" #include "tlString.h" #include "tlLog.h" #include "tlFileUtils.h" #include "dbShapeProcessor.h" #include #include namespace db { // --------------------------------------------------------------------------------------- // Some utilities static void parse_format (const std::string &format, int &l, int &t, bool &tz) { tl::Extractor ex (format.c_str ()); try { l = -1; t = -1; tz = true; if (! ex.at_end ()) { if (! ex.test ("*")) { ex.try_read (l); } ex.expect (":"); if (! ex.test ("*")) { ex.try_read (t); } if (ex.test ("T") || ex.test ("t")) { tz = true; } else if (ex.test ("L") || ex.test ("l")) { tz = false; } else { if (t < 0) { tz = false; } } ex.expect_end (); } } catch (tl::Exception &ex) { throw tl::Exception (tl::to_string (tr ("Gerber format specification '%s' is invalid: %s")), format, ex.msg ()); } } static std::string format_to_string (int l, int t, bool tz) { std::string r; if (l >= 0) { r += tl::to_string (l); } else { r += "*"; } r += ":"; if (t >= 0) { r += tl::to_string (t); } else { r += "*"; } r += (tz ? "T" : "L"); return r; } // --------------------------------------------------------------------------------------- // Implementation of GerberFile GerberFileReader::GerberFileReader () : m_circle_points (64), m_digits_before (-1), m_digits_after (-1), m_omit_leading_zeroes (true), m_merge (false), m_inverse (false), m_dbu (0.001), m_unit (1000.0), m_rot (0.0), m_s (1.0), m_ox (0.0), m_oy (0.0), m_mx (false), m_my (false), m_orot (0.0), m_os (1.0), m_omx (false), m_omy (false), m_ep (true /*report progress*/), mp_layout (0), mp_top_cell (0), mp_stream (0), m_progress (tl::to_string (tr ("Reading Gerber file")), 10000) { m_progress.set_format (tl::to_string (tr ("%.0f MB"))); m_progress.set_unit (1024 * 1024); } bool GerberFileReader::accepts (tl::TextInputStream &stream) { mp_stream = &stream; bool result = does_accept (); mp_stream = 0; return result; } GerberMetaData GerberFileReader::scan (tl::TextInputStream &stream) { mp_stream = &stream; mp_layout = 0; mp_top_cell = 0; m_target_layers.clear (); GerberMetaData meta_data; try { meta_data = do_scan(); } catch (tl::Exception &ex) { throw tl::Exception (ex.msg () + tl::to_string (tr (" in line ")) + tl::to_string (stream.line_number ())); } mp_stream = 0; return meta_data; } void GerberFileReader::read (tl::TextInputStream &stream, db::Layout &layout, db::Cell &cell, const std::vector &targets) { GraphicsState state; state.global_trans = m_global_trans; swap_graphics_state (state); mp_stream = &stream; mp_layout = &layout; mp_top_cell = &cell; m_target_layers = targets; try { do_read (); } catch (tl::BreakException &) { throw; } catch (tl::Exception &ex) { throw tl::Exception (ex.msg () + tl::to_string (tr (" in line ")) + tl::to_string (stream.line_number ())); } flush (); mp_stream = 0; m_target_layers.clear (); } void GerberFileReader::set_format_string (const std::string &format) { int l = -1, t = -1; bool tz = true; parse_format (format, l, t, tz); set_format (l, t, tz); } std::string GerberFileReader::format_string () const { return format_to_string (m_digits_before, m_digits_after, m_omit_leading_zeroes); } void GerberFileReader::warn (const std::string &warning) { tl::warn << warning << tl::to_string (tr (" in line ")) << mp_stream->line_number () << tl::to_string (tr (" (file ")) << mp_stream->source () << ")"; } void GerberFileReader::error (const std::string &error) { tl::error << error << tl::to_string (tr (" in line ")) << mp_stream->line_number () << tl::to_string (tr (" (file ")) << mp_stream->source () << ")"; } void GerberFileReader::fatal (const std::string &error) { throw tl::Exception (error); } double GerberFileReader::accuracy() const { return m_unit / pow (10.0, m_digits_after); } double GerberFileReader::read_coord (tl::Extractor &ex) { ex.skip (); int sign = 1; if (*ex == '+') { ++ex; } else if (*ex == '-') { sign = -1; ++ex; } double number = 0.0; int ndigits = 0; bool has_dot = false; while (! ex.at_end () && (isdigit (*ex) || *ex == '.')) { if (*ex == '.') { has_dot = true; ndigits = 0; } else { number = number * 10.0 + (*ex - '0'); ++ndigits; } ++ex; } if (has_dot) { number /= pow (10.0, ndigits); } else if (m_omit_leading_zeroes) { if (m_digits_after < 0) { error (tl::to_string (tr ("Undefined number of digits - format missing"))); } number /= pow (10.0, m_digits_after); } else { if (m_digits_before < 0) { error (tl::to_string (tr ("Undefined number of digits - format missing"))); } number /= pow (10.0, ndigits - m_digits_before); } return number * m_unit * sign; } void GerberFileReader::swap_graphics_state (GraphicsState &state) { std::swap (m_inverse, state.inverse); std::swap (m_global_trans, state.global_trans); std::swap (m_s, state.m_s); std::swap (m_mx, state.m_mx); std::swap (m_my, state.m_my); std::swap (m_ox, state.m_ox); std::swap (m_oy, state.m_oy); std::swap (m_rot, state.m_rot); std::swap (m_os, state.m_os); std::swap (m_omx, state.m_omx); std::swap (m_omy, state.m_omy); std::swap (m_orot, state.m_orot); std::swap (m_lines, state.lines); std::swap (m_polygons, state.polygons); std::swap (m_clear_polygons, state.clear_polygons); std::swap (m_displacements, state.displacements); } void GerberFileReader::push_state (const std::string &token) { m_graphics_stack.push_back (GraphicsState ()); swap_graphics_state (m_graphics_stack.back ()); m_graphics_stack.back ().token = token; } std::string GerberFileReader::pop_state () { std::string token; if (! m_graphics_stack.empty ()) { swap_graphics_state (m_graphics_stack.back ()); token = m_graphics_stack.back ().token; } m_graphics_stack.pop_back (); return token; } bool GerberFileReader::graphics_stack_empty () const { return m_graphics_stack.empty (); } db::DCplxTrans GerberFileReader::local_trans () const { // TODO: is this order correct? db::DCplxTrans lt = db::DCplxTrans (m_s, m_rot, false, db::DVector (m_ox, m_oy)); if (m_mx) { lt *= db::DCplxTrans (db::DTrans (db::FTrans::m0)); } if (m_my) { lt *= db::DCplxTrans (db::DTrans (db::FTrans::m90)); } return lt; } db::DCplxTrans GerberFileReader::object_trans () const { db::DCplxTrans ot = db::DCplxTrans (m_os, m_orot, false, db::DVector ()); if (m_omx) { ot *= db::DCplxTrans (db::DTrans (db::FTrans::m0)); } if (m_omy) { ot *= db::DCplxTrans (db::DTrans (db::FTrans::m90)); } return ot; } void GerberFileReader::produce_line (const db::DPath &p, bool clear) { db::DCplxTrans t = global_trans () * db::DCplxTrans (1.0 / dbu ()) * local_trans (); // Ignore clear paths for now - they cannot be subtracted from anything. // Clear is just provided for completeness. if (clear) { return; } process_clear_polygons (); for (std::vector::const_iterator d = m_displacements.begin (); d != m_displacements.end (); ++d) { m_lines.push_back (db::Path ()); m_lines.back() = db::Path (p.transformed (t * db::DCplxTrans (*d))); } } void GerberFileReader::produce_polygon (const db::DPolygon &p, bool clear) { db::DCplxTrans t = global_trans () * db::DCplxTrans (1.0 / dbu ()) * local_trans (); if (! clear) { process_clear_polygons (); } for (std::vector::const_iterator d = m_displacements.begin (); d != m_displacements.end (); ++d) { db::Polygon *poly = 0; if (clear) { m_clear_polygons.push_back (db::Polygon ()); poly = &m_clear_polygons.back (); } else { m_polygons.push_back (db::Polygon ()); poly = &m_polygons.back (); } *poly = db::Polygon (p.transformed (t * db::DCplxTrans (*d))); } } void GerberFileReader::process_clear_polygons () { if (! m_clear_polygons.empty ()) { std::vector input; m_polygons.swap (input); m_ep.boolean (input, m_clear_polygons, m_polygons, db::BooleanOp::ANotB, false, true); m_clear_polygons.clear (); } } void GerberFileReader::collect (db::Region ®ion) { process_clear_polygons (); if (m_merge) { std::vector merged_polygons; m_ep.merge (m_polygons, merged_polygons, 0, false /*don't resolve holes*/); m_polygons.swap (merged_polygons); } for (std::vector::const_iterator p = m_polygons.begin (); p != m_polygons.end (); ++p) { region.insert (*p); } for (std::vector::const_iterator p = m_lines.begin (); p != m_lines.end (); ++p) { region.insert (*p); } m_polygons.clear (); m_lines.clear (); } void GerberFileReader::flush () { process_clear_polygons (); if (m_merge) { std::vector merged_polygons; m_ep.merge (m_polygons, merged_polygons, 0, false /*don't resolve holes*/); m_polygons.swap (merged_polygons); } for (std::vector ::const_iterator t = m_target_layers.begin (); t != m_target_layers.end (); ++t) { db::Shapes &shapes = mp_top_cell->shapes (*t); for (std::vector::const_iterator p = m_polygons.begin (); p != m_polygons.end (); ++p) { shapes.insert (*p); } for (std::vector::const_iterator p = m_lines.begin (); p != m_lines.end (); ++p) { shapes.insert (*p); } } m_polygons.clear (); m_lines.clear (); } void GerberFileReader::progress_checkpoint () { if (mp_stream) { m_progress.set (mp_stream->raw_stream ().pos ()); } } void GerberFileReader::step_and_repeat (const std::vector &displacements) { reset_step_and_repeat (); if (! displacements.empty ()) { m_displacements = displacements; } } void GerberFileReader::reset_step_and_repeat () { m_displacements.clear (); m_displacements.push_back (db::DVector ()); } // --------------------------------------------------------------------------------------- // Implementation of GerberFile GerberFile::GerberFile () : m_circle_points (-1), m_merge_mode (-1), m_digits_before (-1), m_digits_after (-1), m_omit_leading_zeroes (true) { // .. nothing yet .. } void GerberFile::set_format_string (const std::string &format) { int l = -1, t = -1; bool tz = true; parse_format (format, l, t, tz); set_format (l, t, tz); } std::string GerberFile::format_string () const { return format_to_string (digits_before (), digits_after (), omit_leading_zeroes ()); } void GerberFile::set_layers_string (const std::string &layers) { tl::Extractor ex (layers.c_str ()); while (! ex.at_end ()) { db::LayerProperties lp; lp.read (ex); add_layer_spec (lp); ex.test (","); } } std::string GerberFile::layers_string () const { std::string r; for (std::vector::const_iterator ls = m_layer_specs.begin (); ls != m_layer_specs.end (); ++ls) { if (! r.empty ()) { r += ","; } r += ls->to_string (); } return r; } // --------------------------------------------------------------------------------------- // Implementation of GerberImporter // TODO: generalize this: std::vector > get_readers () { std::vector > readers; readers.push_back (new db::GerberDrillFileReader ()); readers.push_back (new db::RS274XReader ()); return readers; } GerberImporter::GerberImporter () : m_cell_name ("PCB"), m_dbu (0.001), m_merge (false), m_invert_negative_layers (false), m_border (5000), m_circle_points (64) { // .. nothing yet .. } void GerberImporter::load_project (const std::string &fn) { // use the file's absolute path as the base directory m_dir = tl::absolute_file_path (fn); tl::InputStream stream (fn); tl::TextInputStream text_stream (stream); load_project (text_stream); } void GerberImporter::load_project (tl::TextInputStream &stream) { try { do_load_project (stream); } catch (tl::Exception &ex) { throw tl::Exception (ex.msg () + tl::to_string (tr (" in line ")) + tl::to_string (stream.line_number ())); } } GerberMetaData GerberImporter::scan (const std::string &fn) { tl::InputStream stream (fn); tl::TextInputStream text_stream (stream); return scan (text_stream); } GerberMetaData GerberImporter::scan (tl::TextInputStream &stream) { try { std::vector > readers = get_readers (); // determine the reader to use: for (std::vector >::iterator r = readers.begin (); r != readers.end (); ++r) { stream.reset (); if ((*r)->accepts (stream)) { return (*r)->scan (stream); } } } catch (tl::Exception &ex) { tl::warn << ex.msg (); } return GerberMetaData (); } static void read_ref_point_spec (tl::Extractor &l, std::vector > &ref_points, size_t n, bool pcb) { while (ref_points.size () < n + 1) { ref_points.push_back (std::pair ()); } l.expect ("="); double x, y; l.read (x); l.test (","); l.read (y); if (pcb) { ref_points[n].first = db::DBox (x, y, x, y); } else { ref_points[n].second = db::DBox (x, y, x, y); } } void GerberImporter::do_load_project (tl::TextInputStream &stream) { std::vector > ref_points; // Reference points PCB/Layout: boxes can be "empty" .. while (! stream.at_end ()) { std::string lstr = stream.get_line (); tl::Extractor l (lstr.c_str ()); // skip empty or pure comment lines if (l.at_end () || l.test ("#")) { continue; } if (l.test ("dbu")) { l.expect ("="); l.read (m_dbu); if (m_dbu < 1e-6) { throw tl::Exception (tl::to_string (tr ("Invalid database unit %g")), m_dbu); } } else if (l.test ("cell-name")) { l.expect ("="); l.read_word_or_quoted (m_cell_name); } else if (l.test ("layer-styles")) { l.expect ("="); l.read_word_or_quoted (m_layer_styles, "%!.:/\\+-,=_$"); } else if (l.test ("circle-points")) { l.expect ("="); l.read (m_circle_points); if (m_circle_points < 4) { throw tl::Exception (tl::to_string (tr ("Invalid number of points for full circle (%d)")), m_circle_points); } } else if (l.test ("keep-path")) { // ignored currently, kept for compatibility with prototype l.expect ("="); int d; l.read (d); } else if (l.test ("border")) { l.expect ("="); double d; l.read (d); m_border = d; } else if (l.test ("invert-negative-layers")) { l.expect ("="); int d; l.read (d); m_invert_negative_layers = d; } else if (l.test ("merge")) { l.expect ("="); int d; l.read (d); m_merge = d; // provided for compatibility with prototype, use ref-point instead } else if (l.test ("p1-pcb")) { read_ref_point_spec (l, ref_points, 0, true); } else if (l.test ("p2-pcb")) { read_ref_point_spec (l, ref_points, 1, true); } else if (l.test ("p3-pcb")) { read_ref_point_spec (l, ref_points, 2, true); } else if (l.test ("p1-gds")) { read_ref_point_spec (l, ref_points, 0, false); } else if (l.test ("p2-gds")) { read_ref_point_spec (l, ref_points, 1, false); } else if (l.test ("p3-gds")) { read_ref_point_spec (l, ref_points, 2, false); } else if (l.test ("ref-point")) { l.expect ("="); l.expect ("("); double x_pcb, y_pcb; l.read (x_pcb); l.expect (","); l.read (y_pcb); l.expect (")"); l.expect (","); l.expect ("("); double x_ly, y_ly; l.read (x_ly); l.expect (","); l.read (y_ly); l.expect (")"); ref_points.push_back (std::make_pair (db::DBox (x_pcb, y_pcb, x_pcb, y_pcb), db::DBox (x_ly, y_ly, x_ly, y_ly))); } else if (l.test ("transformation")) { l.expect ("="); db::DCplxTrans gt; l.read (gt); m_global_trans = gt * m_global_trans; } else if (l.test ("mirror")) { l.expect ("="); int d; l.read (d); if (d != 0) { m_global_trans = db::DCplxTrans (db::DFTrans::m0) * m_global_trans; } } else if (l.test ("dir")) { l.expect ("="); l.read_word_or_quoted (m_dir, "%!.:/\\+-,=_$"); } else if (l.test ("format")) { l.expect ("="); l.read_word_or_quoted (m_format_string, "*:"); // check the string ... int l = 0, t = 0; bool tz = false; parse_format (m_format_string, l, t, tz); } else { l.test ("file"); db::GerberFile file; std::string fn; l.read_word_or_quoted (fn, "%!.:/\\+-,=_$"); file.set_filename (fn); while (! l.at_end () && ! l.test ("#")) { if (l.test ("circle-points")) { l.expect ("="); int cp = -1; l.read (cp); if (cp < 4) { throw tl::Exception (tl::to_string (tr ("Invalid number of points for full circle (%d)")), m_circle_points); } file.set_circle_points (cp); } else if (l.test ("format")) { l.expect ("="); std::string f; l.read_word_or_quoted (f, "*:"); file.set_format_string (f); } else if (l.test ("merge")) { l.expect ("="); int m = 0; l.read (m); file.set_merge_mode (m != 0 ? 1 : 0); } else { db::LayerProperties lp; lp.read (l); file.add_layer_spec (lp); } } add_file (file); } if (! l.test ("#")) { l.expect_end (); } } // transfer the reference points m_reference_points.clear (); if (ref_points.size () > 3) { throw tl::Exception (tl::to_string (tr ("Not more than three reference points can be specified"))); } for (unsigned int i = 0; i < ref_points.size (); ++i) { if (ref_points [i].first.empty () || ref_points [i].second.empty ()) { throw tl::Exception (tl::to_string (tr ("Reference point #%d is not fully specified (either PCB or layout coordinate is missing)")), int (i + 1)); } m_reference_points.push_back (std::make_pair (ref_points [i].first.center (), ref_points [i].second.center ())); } } void GerberImporter::save_project (std::ostream &stream) { stream << "# Gerber PCB import project" << std::endl; stream << "# Created by KLayout" << std::endl; stream << "dir=" << tl::to_quoted_string (m_dir) << std::endl; stream << "cell=" << tl::to_quoted_string (m_cell_name) << std::endl; stream << "dbu=" << tl::to_string (m_dbu) << std::endl; stream << "circle-points=" << tl::to_string (m_circle_points) << std::endl; stream << "transformation=" << tl::to_quoted_string (m_global_trans.to_string ()) << std::endl; for (std::vector >::const_iterator r = m_reference_points.begin (); r != m_reference_points.end (); ++r) { stream << "ref-point=(" << tl::to_string (r->first.x ()) << "," << tl::to_string (r->first.y ()) << "),(" << tl::to_string (r->second.x ()) << "," << tl::to_string (r->second.y ()) << ")" << std::endl; } stream << "merge=" << (m_merge ? 1 : 0) << std::endl; stream << "invert-negative-layers=" << (m_invert_negative_layers ? 1 : 0) << std::endl; stream << "border=" << tl::to_string (m_border) << std::endl; if (! m_layer_styles.empty ()) { stream << "layer-styles=" << tl::to_quoted_string (m_layer_styles) << std::endl; } for (std::vector::iterator file = m_files.begin (); file != m_files.end (); ++file) { stream << "file " << tl::to_quoted_string (file->filename ()); for (std::vector ::const_iterator ls = file->layer_specs ().begin (); ls != file->layer_specs ().end (); ++ls) { stream << " " << tl::to_quoted_string (ls->to_string ()); } if (file->circle_points () > 0) { stream << " circle-points=" << tl::to_string (file->circle_points ()); } if (file->has_format ()) { stream << " format=" << tl::to_quoted_string (file->format_string ()); } if (file->merge_mode () >= 0) { stream << " merge=" << tl::to_string (file->merge_mode ()); } stream << std::endl; } } void GerberImporter::read (db::Layout &layout, db::cell_index_type cell_index) { m_cell_name = layout.cell_name (cell_index); m_dbu = layout.dbu (); do_read (layout, cell_index); } db::cell_index_type GerberImporter::read (db::Layout &layout) { db::cell_index_type ci = layout.add_cell (m_cell_name.c_str ()); layout.dbu (m_dbu); do_read (layout, ci); return ci; } void GerberImporter::do_read (db::Layout &layout, db::cell_index_type cell_index) { tl::log << tl::to_string (tr ("Importing PCB data")); std::set inverse_layers; { tl::RelativeProgress progress (tl::to_string (tr ("Importing PCB data")), m_files.size (), 1); // derive the actual global transformation from the reference points db::DCplxTrans global_trans (m_global_trans); if (! m_reference_points.empty ()) { db::DPoint p1_pcb = m_reference_points[0].first; db::DPoint p1_ly = m_reference_points[0].second; if (m_reference_points.size () > 1) { db::DPoint p2_pcb = m_reference_points[1].first; db::DPoint p2_ly = m_reference_points[1].second; db::DVector d12_pcb = (p2_pcb - p1_pcb) * (1.0 / p2_pcb.distance (p1_pcb)); db::DVector d12_ly = (p2_ly - p1_ly) * (1.0 / p2_ly.distance (p1_ly)); int ru = -1; int rm = -1; for (int f = 0; f < 8; ++f) { db::DVector d12 = db::DTrans (f) * d12_pcb; if ((d12 - d12_ly).length () < 0.1) { if (f < 4) { ru = f; } else { rm = f; } } } if (ru < 0 || rm < 0) { throw tl::Exception (tl::to_string (tr ("Unable to deduce rotation from reference points p1 and p2 (PCB and layout)"))); } if (m_reference_points.size () > 2) { db::DPoint p3_pcb = m_reference_points[2].first; db::DPoint p3_ly = m_reference_points[2].second; db::DVector d13_pcb = (p3_pcb - p1_pcb) * (1.0 / p3_pcb.distance (p1_pcb)); db::DVector d13_ly = (p3_ly - p1_ly) * (1.0 / p3_ly.distance (p1_ly)); double vp_pcb = d13_pcb.x () * d12_pcb.y () - d13_pcb.y () * d12_pcb.x (); double vp_gds = d13_ly.x () * d12_ly.y () - d13_ly.y () * d12_ly.x (); if (vp_pcb * vp_gds < 0.0) { global_trans = db::DCplxTrans (db::DFTrans (rm)); } else { global_trans = db::DCplxTrans (db::DFTrans (ru)); } } else { if (global_trans.is_mirror ()) { global_trans = db::DCplxTrans (db::DFTrans (rm)); } else { global_trans = db::DCplxTrans (db::DFTrans (ru)); } } } global_trans = db::DCplxTrans (p1_ly - (db::DPoint () + global_trans.disp ())) * global_trans * db::DCplxTrans (db::DPoint () - p1_pcb); } std::string format (m_format_string); for (std::vector::iterator file = m_files.begin (); file != m_files.end (); ++file) { ++progress; std::vector targets; for (std::vector ::const_iterator ls = file->layer_specs ().begin (); ls != file->layer_specs ().end (); ++ls) { int layer_index = -1; for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) { if (layout.get_properties ((*l).first).log_equal (*ls)) { layer_index = int ((*l).first); break; } } if (layer_index < 0) { layer_index = int (layout.insert_layer (*ls)); } targets.push_back (layer_index); } std::string fp = tl::combine_path (tl::absolute_file_path (m_dir), file->filename ()); tl::InputStream input_file (fp); tl::TextInputStream stream (input_file); std::vector > readers = get_readers (); // determine the reader to use: db::GerberFileReader *reader = 0; for (std::vector >::iterator r = readers.begin (); r != readers.end (); ++r) { stream.reset (); if ((*r)->accepts (stream)) { reader = r->operator-> (); break; } } if (! reader) { throw tl::Exception (tl::to_string (tr ("Unable to determine format for file '%s'")), fp); } stream.reset (); // set up the reader reader->set_dbu (m_dbu); reader->set_global_trans (db::DCplxTrans (1.0 / m_dbu) * global_trans * db::DCplxTrans (m_dbu)); reader->set_format_string (file->format_string ()); if (! reader->has_format ()) { reader->set_format_string (format); } reader->set_merge (file->merge_mode () >= 0 ? (file->merge_mode () != 0) : m_merge); reader->set_circle_points (file->circle_points () >= 0 ? file->circle_points () : m_circle_points); // actually read try { tl::log << "Reading PCB file '" << file->filename () << "' with format '" << file->format_string () << "'"; reader->read (stream, layout, layout.cell (cell_index), targets); } catch (tl::BreakException &) { throw; } catch (tl::Exception &ex) { throw tl::Exception (ex.msg () + ", reading file " + file->filename ()); } // use the current format as further default format = reader->format_string (); if (reader->is_inverse ()) { inverse_layers.insert (targets.begin (), targets.end ()); } } } // Invert the negative layers if requested if (m_invert_negative_layers && ! inverse_layers.empty ()) { db::Cell &cell = layout.cell (cell_index); double e = m_border / layout.dbu (); db::Box bbox = cell.bbox ().enlarged (db::Vector (db::DVector (e, e))); int bbox_layer = layout.insert_layer (); cell.shapes (bbox_layer).insert (bbox); db::ShapeProcessor sp; int n = 0; for (std::set::const_iterator l = inverse_layers.begin (); l != inverse_layers.end (); ++l) { tl::log << "Inverting layer " << layout.get_properties (*l).to_string (); sp.enable_progress (tl::to_string (tr ("Inverting layer")) + " " + tl::to_string (n + 1) + " " + tl::to_string (tr ("of")) + " " + tl::to_string (inverse_layers.size ())); sp.boolean (layout, cell, *l, layout, cell, bbox_layer, cell.shapes (*l), db::BooleanOp::BNotA, true); // clear the result layer for all called cells (if there are some) std::set called_cells; cell.collect_called_cells (called_cells); for (std::set::const_iterator c = called_cells.begin (); c != called_cells.end (); ++c) { layout.cell (*c).shapes (*l).clear (); } } layout.delete_layer (bbox_layer); } } // --------------------------------------------------------------- // Plugin for the stream reader class GerberReader : public db::ReaderBase { public: GerberReader (tl::InputStream &s) : m_stream (s) { // .. nothing yet .. } ~GerberReader () { // .. nothing yet .. } virtual const db::LayerMap &read (db::Layout &layout, const db::LoadLayoutOptions & /*options*/) { // TODO: too simple, should provide at least a layer filtering. return read (layout); } virtual const db::LayerMap &read (db::Layout &layout) { db::GerberImportData data; std::string fn (m_stream.source ()); if (! fn.empty ()) { data.base_dir = tl::absolute_path (fn); } data.load (m_stream); db::GerberImporter importer; data.setup_importer (&importer); importer.read (layout); std::string lyr_file = data.get_layer_properties_file (); if (! lyr_file.empty ()) { layout.add_meta_info (db::MetaInfo ("layer-properties-file", "Layer Properties File", lyr_file)); } return m_layers; } virtual const char *format () const { return "GerberPCB"; } private: tl::InputStream &m_stream; db::LayerMap m_layers; }; class GerberFormatDeclaration : public db::StreamFormatDeclaration { virtual std::string format_name () const { return "GerberPCB"; } virtual std::string format_desc () const { return "Gerber PCB"; } virtual std::string format_title () const { return "Gerber PCB (project files)"; } virtual std::string file_format () const { return "Gerber PCB project files (*.pcb)"; } virtual bool detect (tl::InputStream &stream) const { // The test is that somewhere within the first 1000 bytes, a XML tag appears. // 1000 bytes are within the initial block that the stream reader reads and hence // this does not trigger any reread which is not available on some sources. // TODO: this is pretty simple test. A more elaborate test would be in place here. std::string h = stream.read_all (1000); // HINT: this assumes UTF8 or ISO encoding ... if (h.find ("") != std::string::npos) { return true; } else { return false; } } virtual db::ReaderBase *create_reader (tl::InputStream &s) const { return new GerberReader (s); } virtual db::WriterBase *create_writer () const { return 0; } virtual bool can_read () const { return true; } virtual bool can_write () const { return false; } }; static tl::RegisteredClass format_decl (new GerberFormatDeclaration (), 1000, "GerberPCB"); }