From ff4e99f2a22c3580220345fd77fa543e963a68a8 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 10 Sep 2017 00:24:59 +0200 Subject: [PATCH] Gerber X2: metadata and project initialization from the latter (at least in free mode) --- src/ext/ext/extGerberDrillFileReader.cc | 8 ++ src/ext/ext/extGerberDrillFileReader.h | 1 + src/ext/ext/extGerberImportDialog.cc | 171 +++++++++++++++++++++--- src/ext/ext/extGerberImporter.cc | 75 +++++++++-- src/ext/ext/extGerberImporter.h | 119 +++++++++++++++++ src/ext/ext/extRS274XReader.cc | 123 +++++++++++++++++ src/ext/ext/extRS274XReader.h | 1 + src/ext/unit_tests/extGerberImport.cc | 104 ++++++++++++++ 8 files changed, 576 insertions(+), 26 deletions(-) diff --git a/src/ext/ext/extGerberDrillFileReader.cc b/src/ext/ext/extGerberDrillFileReader.cc index 72d6c70de..072b77bb3 100644 --- a/src/ext/ext/extGerberDrillFileReader.cc +++ b/src/ext/ext/extGerberDrillFileReader.cc @@ -88,6 +88,14 @@ GerberDrillFileReader::init () m_format_set = false; } +GerberMetaData +GerberDrillFileReader::do_scan () +{ + GerberMetaData data; + data.function = GerberMetaData::Hole; + return data; +} + void GerberDrillFileReader::do_read () { diff --git a/src/ext/ext/extGerberDrillFileReader.h b/src/ext/ext/extGerberDrillFileReader.h index d5388e01f..d0842d04f 100644 --- a/src/ext/ext/extGerberDrillFileReader.h +++ b/src/ext/ext/extGerberDrillFileReader.h @@ -69,6 +69,7 @@ public: protected: virtual void do_read (); + GerberMetaData do_scan (); private: std::string m_buffer; diff --git a/src/ext/ext/extGerberImportDialog.cc b/src/ext/ext/extGerberImportDialog.cc index 18a70ec4e..f704bea38 100644 --- a/src/ext/ext/extGerberImportDialog.cc +++ b/src/ext/ext/extGerberImportDialog.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include @@ -1340,10 +1341,134 @@ GerberImportDialog::layout_layer_double_clicked (QTreeWidgetItem *, int) } } +struct FilePositionCompare +{ + bool operator() (const std::pair &a, const std::pair &b) + { + if ((a.first.cu_layer_number == 0) != (b.first.cu_layer_number == 0)) { + return (a.first.cu_layer_number == 0) < (b.first.cu_layer_number == 0); + } + if (a.first.cu_layer_number != b.first.cu_layer_number) { + return a.first.cu_layer_number < b.first.cu_layer_number; + } + if ((a.first.from_cu == 0) != (b.first.from_cu == 0)) { + return (a.first.from_cu == 0) < (b.first.from_cu == 0); + } + if (a.first.from_cu != b.first.from_cu) { + return a.first.from_cu < b.first.from_cu; + } + return a.second < b.second; + } +}; + void GerberImportDialog::enter_page () { - // .. nothing yet .. + int page = mp_ui->central_stack->currentIndex (); + + if (page == 5) { + + // --- Free Files page + + if (mp_data->free_files.empty ()) { + + // scan the files in the directory and populate the file list + + QDir dir (tl::to_qstring (mp_data->base_dir)); + if (dir.exists ()) { + + QStringList filters; + filters << tl::to_qstring ("*.gbr"); + filters << tl::to_qstring ("*.GBR"); + + std::vector > files; + + QStringList entries = dir.entryList (filters); + for (QStringList::const_iterator e = entries.begin (); e != entries.end (); ++e) { + ext::GerberMetaData data = ext::GerberImporter::scan (tl::to_string (dir.filePath (*e))); + files.push_back (std::make_pair (data, tl::to_string (*e))); + } + + std::sort (files.begin (), files.end (), FilePositionCompare ()); + + if (files.empty ()) { + return; + } + if (QMessageBox::question (this, tr ("Populate Project"), tr ("Some files have been found in the specified base directory.\nIf these files contain file attributes, the project can be initialized properly.\n\nPopulate project from these files?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) { + return; + } + + mp_data->layout_layers.clear (); + + int min_layer = 0, max_layer = 0; + for (std::vector >::const_iterator f = files.begin (); f != files.end (); ++f) { + if (f->first.cu_layer_number > 0) { + if (min_layer == 0 || min_layer > f->first.cu_layer_number) { + min_layer = f->first.cu_layer_number; + } + if (max_layer == 0 || max_layer < f->first.cu_layer_number) { + max_layer = f->first.cu_layer_number; + } + } + } + + std::map l2l; + std::map l2v; + + if (min_layer > 0) { + for (int l = min_layer; l <= max_layer; ++l) { + l2l.insert (std::make_pair (l, int (mp_data->layout_layers.size ()))); + mp_data->layout_layers.push_back (db::LayerProperties (l * 2, 0, "Cu" + tl::to_string (l))); + if (l < max_layer) { + l2v.insert (std::make_pair (l, int (mp_data->layout_layers.size ()))); + mp_data->layout_layers.push_back (db::LayerProperties (l * 2 + 1, 0, "Via" + tl::to_string (l))); + } + } + } + + int next_layer = max_layer * 2; + int hole_num = 0, profile_num = 0, legend_num = 0, solder_num = 0; + + for (std::vector >::const_iterator f = files.begin (); f != files.end (); ++f) { + + mp_data->free_files.push_back (GerberFreeFileDescriptor ()); + mp_data->free_files.back ().filename = tl::to_string (f->second); + + std::vector layers; + + if (f->first.function == ext::GerberMetaData::Copper) { + if (l2l.find (f->first.cu_layer_number) != l2l.end ()) { + layers.push_back (l2l [f->first.cu_layer_number]); + } + } else if (f->first.function == ext::GerberMetaData::PlatedHole) { + for (int l = std::min (f->first.from_cu, f->first.to_cu); l < std::max (f->first.from_cu, f->first.to_cu); ++l) { + if (l2v.find (l) != l2v.end ()) { + layers.push_back (l2v [l]); + } + } + } else if (f->first.function == ext::GerberMetaData::NonPlatedHole || f->first.function == ext::GerberMetaData::NonPlatedHole) { + layers.push_back (int (mp_data->layout_layers.size ())); + mp_data->layout_layers.push_back (db::LayerProperties (++next_layer, 0, "Hole" + tl::to_string (++hole_num))); + } else if (f->first.function == ext::GerberMetaData::Profile) { + layers.push_back (int (mp_data->layout_layers.size ())); + mp_data->layout_layers.push_back (db::LayerProperties (++next_layer, 0, "Profile" + tl::to_string (++profile_num))); + } else if (f->first.function == ext::GerberMetaData::Legend) { + layers.push_back (int (mp_data->layout_layers.size ())); + mp_data->layout_layers.push_back (db::LayerProperties (++next_layer, 0, "Legend" + tl::to_string (++legend_num))); + } else if (f->first.function == ext::GerberMetaData::SolderMask) { + layers.push_back (int (mp_data->layout_layers.size ())); + mp_data->layout_layers.push_back (db::LayerProperties (++next_layer, 0, "SolderMask" + tl::to_string (++solder_num))); + } + + mp_data->free_files.back ().layout_layers = layers; + + } + + } + + } + + } } void @@ -1499,19 +1624,27 @@ GerberImportDialog::commit_page () */ // add layers for all free files if no layer is defined yet. Add additional layers. Try to find some useful numbering scheme. - if (mp_data->layout_layers.size () < mp_data->free_files.size ()) { - int max_layer = 0; - for (std::vector ::const_iterator l = mp_data->layout_layers.begin (); l != mp_data->layout_layers.end (); ++l) { - max_layer = std::max (max_layer, l->layer); + int max_layer = 0; + for (std::vector ::const_iterator l = mp_data->layout_layers.begin (); l != mp_data->layout_layers.end (); ++l) { + max_layer = std::max (max_layer, l->layer); + } + + for (size_t i = 0; i < mp_data->free_files.size (); ++i) { + + std::vector valid_layers; + for (std::vector::const_iterator l = mp_data->free_files[i].layout_layers.begin (); l != mp_data->free_files[i].layout_layers.end (); ++l) { + if (*l >= 0 && *l < int (mp_data->layout_layers.size ())) { + valid_layers.push_back (*l); + } } - while (mp_data->layout_layers.size () < mp_data->free_files.size ()) { + mp_data->free_files[i].layout_layers = valid_layers; - // Add a stupid 1:1 mapping if no layers are mapped for the next file. - if (mp_data->free_files[mp_data->layout_layers.size ()].layout_layers.empty ()) { - mp_data->free_files[mp_data->layout_layers.size ()].layout_layers.push_back (int (mp_data->layout_layers.size ())); - } + // Add a stupid 1:1 mapping if no layers are mapped for the next file. + if (valid_layers.empty ()) { + + mp_data->free_files[i].layout_layers.push_back (int (mp_data->layout_layers.size ())); mp_data->layout_layers.push_back (db::LayerProperties ()); mp_data->layout_layers.back ().layer = ++max_layer; @@ -1927,6 +2060,7 @@ GerberImportDialog::update () if (! l->filename.empty ()) { item->setData (0, Qt::DisplayRole, QVariant (tl::to_qstring (l->filename))); + item->setData (0, Qt::ToolTipRole, QVariant (tl::to_qstring (l->filename))); QFileInfo file_info (tl::to_qstring (l->filename)); if (! mp_data->base_dir.empty () && ! file_info.isAbsolute ()) { @@ -1972,15 +2106,20 @@ GerberImportDialog::update () for (std::vector ::const_iterator l = mp_data->layout_layers.begin (); l != mp_data->layout_layers.end (); ++l, ++n) { QString hdr_label; - QString text = tl::to_qstring (l->to_string ()); - for (const QChar *c = text.constData (); !c->isNull (); ++c) { - if (!hdr_label.isEmpty ()) { - hdr_label += QString::fromUtf8 ("\n"); - } - hdr_label += *c; + + db::LayerProperties ll = *l; + ll.name.clear (); + hdr_label = tl::to_qstring (ll.to_string ()); + hdr_label += QString::fromUtf8 ("\n"); + + if (l->name.size () > 4) { + hdr_label += tl::to_qstring (std::string (l->name, 0, 4) + "..."); + } else { + hdr_label += tl::to_qstring (l->name); } mp_ui->free_layer_mapping_tree->headerItem ()->setData (n + 1, Qt::DisplayRole, QVariant (hdr_label)); + mp_ui->free_layer_mapping_tree->headerItem ()->setData (n + 1, Qt::ToolTipRole, QVariant (tl::to_qstring (l->to_string ()))); if (mp_ui->free_layer_mapping_tree->itemDelegateForColumn (n + 1) == 0) { mp_ui->free_layer_mapping_tree->setItemDelegateForColumn (n + 1, new GerberImportDialogNoEditDelegate (mp_ui->free_layer_mapping_tree)); diff --git a/src/ext/ext/extGerberImporter.cc b/src/ext/ext/extGerberImporter.cc index f047354c6..40e81d1f2 100644 --- a/src/ext/ext/extGerberImporter.cc +++ b/src/ext/ext/extGerberImporter.cc @@ -130,7 +130,28 @@ GerberFileReader::accepts (tl::TextInputStream &stream) return result; } -void +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 (QObject::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; @@ -495,6 +516,15 @@ GerberFile::layers_string () const // --------------------------------------------------------------------------------------- // Implementation of GerberImporter +// TODO: generalize this: +std::vector > get_readers () +{ + std::vector > readers; + readers.push_back (new ext::GerberDrillFileReader ()); + readers.push_back (new ext::RS274XReader ()); + return readers; +} + GerberImporter::GerberImporter () : m_cell_name ("PCB"), m_dbu (0.001), m_merge (false), m_invert_negative_layers (false), m_border (5000), @@ -526,6 +556,37 @@ GerberImporter::load_project (tl::TextInputStream &stream) } } +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) { @@ -926,20 +987,14 @@ GerberImporter::do_read (db::Layout &layout, db::cell_index_type cell_index) tl::InputStream input_file (tl::to_string (fi.absoluteFilePath ())); tl::TextInputStream stream (input_file); - // TODO: generalize this: - ext::RS274XReader rs274x_reader; - ext::GerberDrillFileReader drill_file_reader; - - std::vector readers; - readers.push_back (&drill_file_reader); - readers.push_back (&rs274x_reader); + std::vector > readers = get_readers (); // determine the reader to use: ext::GerberFileReader *reader = 0; - for (std::vector ::const_iterator r = readers.begin (); r != readers.end (); ++r) { + for (std::vector >::iterator r = readers.begin (); r != readers.end (); ++r) { stream.reset (); if ((*r)->accepts (stream)) { - reader = *r; + reader = r->operator-> (); break; } } diff --git a/src/ext/ext/extGerberImporter.h b/src/ext/ext/extGerberImporter.h index f5f795105..57d915bf2 100644 --- a/src/ext/ext/extGerberImporter.h +++ b/src/ext/ext/extGerberImporter.h @@ -49,6 +49,104 @@ namespace lay namespace ext { +/** + * @brief A structure holding the meta data for a Gerber (X2) file + */ +struct GerberMetaData +{ + /** + * @brief Identfies the function of the layer + */ + enum Function + { + NoFunction = 0, + Copper, + Hole, + PlatedHole, + NonPlatedHole, + Profile, + SolderMask, + Legend, + Other + }; + + /** + * @brief Identfies the position of the layer + */ + enum Position + { + NoPosition = 0, + Bottom, + Inner, + Top + }; + + /** + * @brief Constructor + */ + GerberMetaData () + : function (NoFunction), + cu_layer_number (0), + from_cu (0), + to_cu (0), + position (NoPosition) + { } + + /** + * @brief The project name or an empty string if none is given + */ + std::string project_id; + + /** + * @brief The creation date or an empty string if none is given + */ + std::string creation_date; + + /** + * @brief The generation software or an empty string if none is given + */ + std::string generation_software; + + /** + * @brief The function of the layer + */ + Function function; + + /** + * @brief The copper layer number + * + * This is a number identifying the layer in the copper stack. + * The topmost layer is 1, the bottom layer 2 or larger. + * This value is 0 if no layer is specified. + */ + int cu_layer_number; + + /** + * @brief The drill hole start copper layer + * + * This is number of the copper layer that the drill hole connects (upper layer). + * This number is applicable only if the function is PlatedHole or NonPlatedHole. + * It is a value > 0. + */ + int from_cu; + + /** + * @brief The drill hole end copper layer + * + * This is number of the copper layer that the drill hole connects (lower layer). + * This number is applicable only if the function is PlatedHole or NonPlatedHole. + * It is a value > 0. + */ + int to_cu; + + /** + * @brief This is the position of the layer in the stack + * + * This value is applicable for Copper, SolderMask and Legend. + */ + Position position; +}; + /** * @brief A class holding the graphics state of the reader */ @@ -83,6 +181,7 @@ struct GraphicsState * @brief The base class for all readers */ class GerberFileReader + : public tl::Object { public: /** @@ -110,6 +209,11 @@ public: */ void read (tl::TextInputStream &stream, db::Layout &layout, db::Cell &cell, const std::vector &targets); + /** + * @brief Scans the stream and extracts the metadata + */ + GerberMetaData scan (tl::TextInputStream &stream); + /** * @brief Sets the number of points for a circle interpolation * @@ -291,6 +395,11 @@ protected: */ virtual void do_read () = 0; + /** + * @brief Scans the stream and returns the metadata + */ + virtual GerberMetaData do_scan () = 0; + /** * @brief Returns true, if the reader accepts the stream */ @@ -671,6 +780,16 @@ public: */ GerberImporter (); + /** + * @brief Scans the given file and extracts the metadata from it + */ + static GerberMetaData scan (const std::string &fn); + + /** + * @brief Scans the given stream and extracts the metadata from it + */ + static GerberMetaData scan (tl::TextInputStream &stream); + /** * @brief Load the project file from the given stream * diff --git a/src/ext/ext/extRS274XReader.cc b/src/ext/ext/extRS274XReader.cc index e2d2a3adc..71dae21d9 100644 --- a/src/ext/ext/extRS274XReader.cc +++ b/src/ext/ext/extRS274XReader.cc @@ -89,6 +89,129 @@ RS274XReader::init () m_current_aperture = 0; } +static GerberMetaData::Position +parse_position (tl::Extractor &ex) +{ + if (ex.test ("Bot")) { + return GerberMetaData::Bottom; + } else if (ex.test ("Top")) { + return GerberMetaData::Top; + } else if (ex.test ("Inr")) { + return GerberMetaData::Inner; + } else { + return GerberMetaData::NoPosition; + } +} + + +GerberMetaData +RS274XReader::do_scan () +{ + GerberMetaData data; + + char c; + + // Actually read: + while ((c = stream ().skip ()) != 0 && !stream ().at_end ()) { + + if (c == '%') { + + stream ().get_char (); + + while (! stream ().at_end () && (c = stream ().skip ()) != '%') { + + std::string param; + param += stream ().get_char (); + if (! stream ().at_end ()) { + param += stream ().get_char (); + } + + std::string bl = get_block (); + + if (param == "TF") { + + // Extract information + tl::Extractor ex (bl); + + if (ex.test (".ProjectId")) { + + ex.test (","); + data.project_id = ex.get (); + + } else if (ex.test (".CreationDate")) { + + ex.test (","); + data.creation_date = ex.get (); + + } else if (ex.test (".GenerationSoftware")) { + + ex.test (","); + data.generation_software = ex.get (); + + } else if (ex.test (".FileFunction")) { + + ex.test (","); + bool plated = false; + + if (ex.test ("Copper")) { + + data.function = GerberMetaData::Copper; + + ex.test (","); + ex.test ("L"); + ex.read (data.cu_layer_number); + ex.test (","); + data.position = parse_position (ex); + + } else if (ex.test ("Profile")) { + + data.function = GerberMetaData::Profile; + + } else if (ex.test ("Soldermask")) { + + data.function = GerberMetaData::SolderMask; + ex.test (","); + data.position = parse_position (ex); + + } else if (ex.test ("Legend")) { + + data.function = GerberMetaData::Legend; + ex.test (","); + data.position = parse_position (ex); + + } else if ((plated = ex.test ("Plated")) || ex.test ("NonPlated")) { + + data.function = plated ? GerberMetaData::PlatedHole : GerberMetaData::NonPlatedHole; + + ex.test (","); + ex.read (data.from_cu); + ex.test (","); + ex.read (data.to_cu); + + } else { + data.function = GerberMetaData::NoFunction; + } + + } + + } + + } + + // eat trailing '%' + if (! stream ().at_end ()) { + stream ().get_char (); + } + + } else { + get_block (); + } + + } + + return data; +} + void RS274XReader::do_read () { diff --git a/src/ext/ext/extRS274XReader.h b/src/ext/ext/extRS274XReader.h index e4200dc0c..c5d81f492 100644 --- a/src/ext/ext/extRS274XReader.h +++ b/src/ext/ext/extRS274XReader.h @@ -60,6 +60,7 @@ public: protected: virtual void do_read (); + GerberMetaData do_scan (); private: bool m_clear; diff --git a/src/ext/unit_tests/extGerberImport.cc b/src/ext/unit_tests/extGerberImport.cc index f4d60777f..c7f5d1cf0 100644 --- a/src/ext/unit_tests/extGerberImport.cc +++ b/src/ext/unit_tests/extGerberImport.cc @@ -27,6 +27,7 @@ #include "dbLoadLayoutOptions.h" #include "dbReader.h" #include "dbTestSupport.h" +#include "extGerberImporter.h" #include "tlUnitTest.h" @@ -51,6 +52,109 @@ static void run_test (tl::TestBase *_this, const char *dir) db::compare_layouts (_this, layout, tl::testsrc_private () + "/testdata/pcb/" + dir + "/au.oas.gz", db::WriteOAS, 1); } +TEST(0_Metadata) +{ + ext::GerberMetaData data; + + std::string fn; + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/1.gbr"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, "2017-09-07T21:37;00+01:00"); + EXPECT_EQ (data.generation_software, "KLayout,0.25"); + EXPECT_EQ (data.function, ext::GerberMetaData::Copper); + EXPECT_EQ (data.position, ext::GerberMetaData::Top); + EXPECT_EQ (data.from_cu, 0); + EXPECT_EQ (data.to_cu, 0); + EXPECT_EQ (data.cu_layer_number, 1); + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/2.gbr"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, "2017-09-07T21:37;00+01:00"); + EXPECT_EQ (data.generation_software, "KLayout,0.25"); + EXPECT_EQ (data.function, ext::GerberMetaData::Copper); + EXPECT_EQ (data.position, ext::GerberMetaData::Bottom); + EXPECT_EQ (data.from_cu, 0); + EXPECT_EQ (data.to_cu, 0); + EXPECT_EQ (data.cu_layer_number, 4); + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/3.gbr"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, "2017-09-07T21:37;00+01:00"); + EXPECT_EQ (data.generation_software, "KLayout,0.25"); + EXPECT_EQ (data.function, ext::GerberMetaData::Copper); + EXPECT_EQ (data.position, ext::GerberMetaData::Inner); + EXPECT_EQ (data.from_cu, 0); + EXPECT_EQ (data.to_cu, 0); + EXPECT_EQ (data.cu_layer_number, 2); + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/10.gbr"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, "2017-09-07T21:37;00+01:00"); + EXPECT_EQ (data.generation_software, "KLayout,0.25"); + EXPECT_EQ (data.function, ext::GerberMetaData::Legend); + EXPECT_EQ (data.position, ext::GerberMetaData::Top); + EXPECT_EQ (data.from_cu, 0); + EXPECT_EQ (data.to_cu, 0); + EXPECT_EQ (data.cu_layer_number, 0); + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/11.gbr"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, "2017-09-07T21:37;00+01:00"); + EXPECT_EQ (data.generation_software, "KLayout,0.25"); + EXPECT_EQ (data.function, ext::GerberMetaData::SolderMask); + EXPECT_EQ (data.position, ext::GerberMetaData::Top); + EXPECT_EQ (data.from_cu, 0); + EXPECT_EQ (data.to_cu, 0); + EXPECT_EQ (data.cu_layer_number, 0); + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/12.gbr"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, "2017-09-07T21:37;00+01:00"); + EXPECT_EQ (data.generation_software, "KLayout,0.25"); + EXPECT_EQ (data.function, ext::GerberMetaData::PlatedHole); + EXPECT_EQ (data.position, ext::GerberMetaData::NoPosition); + EXPECT_EQ (data.from_cu, 1); + EXPECT_EQ (data.to_cu, 4); + EXPECT_EQ (data.cu_layer_number, 0); + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/13.gbr"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, "2017-09-07T21:37;00+01:00"); + EXPECT_EQ (data.generation_software, "KLayout,0.25"); + EXPECT_EQ (data.function, ext::GerberMetaData::NonPlatedHole); + EXPECT_EQ (data.position, ext::GerberMetaData::NoPosition); + EXPECT_EQ (data.from_cu, 1); + EXPECT_EQ (data.to_cu, 4); + EXPECT_EQ (data.cu_layer_number, 0); + + fn = tl::testsrc_private (); + fn += "/testdata/pcb/metadata/20.drl"; + data = ext::GerberImporter::scan (fn); + + EXPECT_EQ (data.creation_date, ""); + EXPECT_EQ (data.generation_software, ""); + EXPECT_EQ (data.function, ext::GerberMetaData::Hole); + EXPECT_EQ (data.position, ext::GerberMetaData::NoPosition); + EXPECT_EQ (data.from_cu, 0); + EXPECT_EQ (data.to_cu, 0); + EXPECT_EQ (data.cu_layer_number, 0); +} + TEST(1) { test_is_long_runner ();