diff --git a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui
index 6be94aaf0..7e3482704 100644
--- a/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui
+++ b/src/plugins/streamers/lefdef/lay_plugin/LEFDEFImportOptionsDialog.ui
@@ -190,7 +190,7 @@
...
-
+
:/clear.png:/clear.png
@@ -204,7 +204,7 @@
...
-
+
:/add.png:/add.png
@@ -218,7 +218,7 @@
...
-
+
:/up.png:/up.png
@@ -232,7 +232,7 @@
...
-
+
:/down.png:/down.png
@@ -298,7 +298,7 @@
buttonBox
-
+
diff --git a/src/plugins/streamers/magic/db_plugin/dbMAG.cc b/src/plugins/streamers/magic/db_plugin/dbMAG.cc
new file mode 100644
index 000000000..4f00a9c18
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAG.cc
@@ -0,0 +1,118 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2019 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 "dbMAG.h"
+#include "dbMAGReader.h"
+#include "dbMAGWriter.h"
+#include "dbStream.h"
+
+#include "tlClassRegistry.h"
+
+namespace db
+{
+
+// ---------------------------------------------------------------
+// MAGDiagnostics implementation
+
+MAGDiagnostics::~MAGDiagnostics ()
+{
+ // .. nothing yet ..
+}
+
+// ---------------------------------------------------------------
+// MAG format declaration
+
+class MAGFormatDeclaration
+ : public db::StreamFormatDeclaration
+{
+public:
+ MAGFormatDeclaration ()
+ {
+ // .. nothing yet ..
+ }
+
+ virtual std::string format_name () const { return "MAG"; }
+ virtual std::string format_desc () const { return "Magic"; }
+ virtual std::string format_title () const { return "MAG (Magic layout format)"; }
+ virtual std::string file_format () const { return "MAG files (*.MAG *.msg *.mag.gz *.MAG.gz)"; }
+
+ virtual bool detect (tl::InputStream &s) const
+ {
+ return s.read_all (5) == "magic";
+ }
+
+ virtual ReaderBase *create_reader (tl::InputStream &s) const
+ {
+ return new db::MAGReader (s);
+ }
+
+ virtual WriterBase *create_writer () const
+ {
+ return new db::MAGWriter ();
+ }
+
+ virtual bool can_read () const
+ {
+ return true;
+ }
+
+ virtual bool can_write () const
+ {
+ return true;
+ }
+
+ virtual tl::XMLElementBase *xml_reader_options_element () const
+ {
+ return new db::ReaderOptionsXMLElement ("cif",
+ tl::make_member (&db::MAGReaderOptions::lambda, "lambda") +
+ tl::make_member (&db::MAGReaderOptions::dbu, "dbu") +
+ tl::make_member (&db::MAGReaderOptions::layer_map, "layer-map") +
+ tl::make_member (&db::MAGReaderOptions::create_other_layers, "create-other-layers") +
+ tl::make_member (&db::MAGReaderOptions::keep_layer_names, "keep-layer-names") +
+ tl::make_element, db::MAGReaderOptions> (&db::MAGReaderOptions::lib_paths, "lib-paths",
+ tl::make_member::const_iterator, std::vector > (&std::vector::begin, &std::vector::end, &std::vector::push_back, "lib-path")
+ )
+ );
+ }
+
+ virtual tl::XMLElementBase *xml_writer_options_element () const
+ {
+// @@@
+ return new db::WriterOptionsXMLElement ("cif",
+ tl::make_member (&db::MAGWriterOptions::dummy_calls, "dummy-calls") +
+ tl::make_member (&db::MAGWriterOptions::blank_separator, "blank-separator")
+ );
+// @@@
+ }
+};
+
+// NOTE: Because MAG has such a high degree of syntactic freedom, the detection is somewhat
+// fuzzy: do MAG at the very end of the detection chain
+static tl::RegisteredClass reader_decl (new MAGFormatDeclaration (), 2200, "MAG");
+
+// provide a symbol to force linking against
+int force_link_MAG = 0;
+
+}
+
+
diff --git a/src/plugins/streamers/magic/db_plugin/dbMAG.h b/src/plugins/streamers/magic/db_plugin/dbMAG.h
new file mode 100644
index 000000000..988fa6858
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAG.h
@@ -0,0 +1,62 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2019 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
+
+*/
+
+
+#ifndef HDR_dbMAG
+#define HDR_dbMAG
+
+#include "dbPoint.h"
+
+#include "tlException.h"
+#include "tlInternational.h"
+#include "tlString.h"
+#include "tlAssert.h"
+
+#include
+#include
+
+namespace db
+{
+
+/**
+ * @brief The diagnostics interface for reporting problems in the reader or writer
+ */
+class MAGDiagnostics
+{
+public:
+ virtual ~MAGDiagnostics ();
+
+ /**
+ * @brief Issue an error with positional information
+ */
+ virtual void error (const std::string &txt) = 0;
+
+ /**
+ * @brief Issue a warning with positional information
+ */
+ virtual void warn (const std::string &txt) = 0;
+};
+
+}
+
+#endif
+
diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGFormat.h b/src/plugins/streamers/magic/db_plugin/dbMAGFormat.h
new file mode 100644
index 000000000..4cf3a4c66
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGFormat.h
@@ -0,0 +1,180 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2019 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
+
+*/
+
+#ifndef HDR_dbMAGFormat
+#define HDR_dbMAGFormat
+
+#include "dbSaveLayoutOptions.h"
+#include "dbLoadLayoutOptions.h"
+#include "dbPluginCommon.h"
+
+namespace db
+{
+
+/**
+ * @brief Structure that holds the MAG specific options for the reader
+ * NOTE: this structure is non-public linkage by intention. This way it's instantiated
+ * in all compile units and the shared object does not need to be linked.
+ */
+class DB_PLUGIN_PUBLIC MAGReaderOptions
+ : public FormatSpecificReaderOptions
+{
+public:
+ /**
+ * @brief The constructor
+ */
+ MAGReaderOptions ()
+ : lambda (1.0),
+ dbu (0.001),
+ create_other_layers (true),
+ keep_layer_names (false)
+ {
+ // .. nothing yet ..
+ }
+
+ /**
+ * @brief Specifies the lambda value
+ *
+ * The lambda value is the basic scaling parameter
+ */
+ double lambda;
+
+ /**
+ * @brief Specify the database unit to produce
+ *
+ * Specify the database unit which the resulting layout will receive.
+ */
+ double dbu;
+
+ /**
+ * @brief Specifies a layer mapping
+ *
+ * If a layer mapping is specified, only the given layers are read.
+ * Otherwise, all layers are read.
+ * Setting "create_other_layers" to true will make the reader
+ * create other layers for all layers not given in the layer map.
+ * Setting an empty layer map and create_other_layers to true effectively
+ * enables all layers for reading.
+ */
+ db::LayerMap layer_map;
+
+ /**
+ * @brief A flag indicating that a new layers shall be created
+ *
+ * If this flag is set to true, layers not listed in the layer map a created
+ * too.
+ */
+ bool create_other_layers;
+
+ /**
+ * @brief A flag indicating whether the names of layers shall be kept as such
+ *
+ * If this flag is set to false (the default), layer name translation happens.
+ * If set to true, translation will not happen.
+ * Name translation will try to extract GDS layer/datatype numbers from the
+ * layer names. If this value is set to true, no name translation happens.
+ */
+ bool keep_layer_names;
+
+ /**
+ * @brief The library paths
+ *
+ * The library paths are the places where library references are looked up from.
+ * Expression interpolation happens inside these paths.
+ * "tech_dir", "tech_file", "tech_name" are variables by which you can refer to
+ * technology parameters. Relative paths will be resolved relative to the current
+ * file read.
+ */
+ std::vector lib_paths;
+
+ /**
+ * @brief Implementation of FormatSpecificReaderOptions
+ */
+ virtual FormatSpecificReaderOptions *clone () const
+ {
+ return new MAGReaderOptions (*this);
+ }
+
+ /**
+ * @brief Implementation of FormatSpecificReaderOptions
+ */
+ virtual const std::string &format_name () const
+ {
+ static const std::string n ("MAG");
+ return n;
+ }
+};
+
+/**
+ * @brief Structure that holds the MAG specific options for the Writer
+ * NOTE: this structure is non-public linkage by intention. This way it's instantiated
+ * in all compile units and the shared object does not need to be linked.
+ */
+class DB_PLUGIN_PUBLIC MAGWriterOptions
+ : public FormatSpecificWriterOptions
+{
+public:
+ /**
+ * @brief The constructor
+ */
+ MAGWriterOptions ()
+ : dummy_calls (false), blank_separator (false)
+ {
+ // .. nothing yet ..
+ }
+
+ /**
+ * @brief A flag indicating whether dummy calls shall be written
+ * If this flag is true, the writer will produce dummy cell calls on global
+ * level for all top cells.
+ */
+ bool dummy_calls;
+
+ /**
+ * @brief A flag indicating whether to use blanks as x/y separators
+ * If this flag is true, blank characters will be used to separate x and y values.
+ * Otherwise comma characters will be used.
+ */
+ bool blank_separator;
+
+ /**
+ * @brief Implementation of FormatSpecificWriterOptions
+ */
+ virtual FormatSpecificWriterOptions *clone () const
+ {
+ return new MAGWriterOptions (*this);
+ }
+
+ /**
+ * @brief Implementation of FormatSpecificWriterOptions
+ */
+ virtual const std::string &format_name () const
+ {
+ static std::string n ("MAG");
+ return n;
+ }
+};
+
+}
+
+#endif
+
diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc
new file mode 100644
index 000000000..938d4342e
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc
@@ -0,0 +1,862 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2019 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 "dbMAGReader.h"
+#include "dbStream.h"
+#include "dbObjectWithProperties.h"
+#include "dbArray.h"
+#include "dbStatic.h"
+
+#include "tlException.h"
+#include "tlString.h"
+#include "tlClassRegistry.h"
+
+#include
+
+namespace db
+{
+
+// ---------------------------------------------------------------
+// MAGReader
+
+
+MAGReader::MAGReader (tl::InputStream &s)
+ : m_stream (s),
+ m_progress (tl::to_string (tr ("Reading MAG file")), 1000),
+ m_lambda (1.0), m_dbu (0.001)
+{
+ m_progress.set_format (tl::to_string (tr ("%.0fk lines")));
+ m_progress.set_format_unit (1000.0);
+ m_progress.set_unit (100000.0);
+}
+
+MAGReader::~MAGReader ()
+{
+ // .. nothing yet ..
+}
+
+const LayerMap &
+MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
+{
+ prepare_layers ();
+
+ const db::MAGReaderOptions &specific_options = options.get_options ();
+ m_lambda = specific_options.lambda;
+ m_dbu = specific_options.dbu;
+ m_lib_paths = specific_options.lib_paths;
+
+ db::LayerMap lm = specific_options.layer_map;
+ lm.prepare (layout);
+ set_layer_map (lm);
+ set_create_layers (specific_options.create_other_layers);
+ set_keep_layer_names (specific_options.keep_layer_names);
+
+ do_read (layout);
+
+ finish_layers (layout);
+ return layer_map ();
+}
+
+const LayerMap &
+MAGReader::read (db::Layout &layout)
+{
+ return read (layout, db::LoadLayoutOptions ());
+}
+
+void
+MAGReader::error (const std::string &msg)
+{
+ throw MAGReaderException (msg, m_stream.line_number (), m_stream.source ());
+}
+
+void
+MAGReader::warn (const std::string &msg)
+{
+ // TODO: compress
+ tl::warn << msg
+ << tl::to_string (tr (" (line=")) << m_stream.line_number ()
+ << tl::to_string (tr (", file=")) << m_stream.source ()
+ << ")";
+}
+
+#if 0 // @@@
+/**
+ * @brief Skip blanks in the sense of MAG
+ * A blank in MAG is "any ASCII character except digit, upperChar, '-', '(', ')', or ';'"
+ */
+void
+MAGReader::skip_blanks()
+{
+ while (! m_stream.at_end ()) {
+ char c = m_stream.peek_char ();
+ if (isupper (c) || isdigit (c) || c == '-' || c == '(' || c == ')' || c == ';') {
+ return;
+ }
+ m_stream.get_char ();
+ }
+}
+
+/**
+ * @brief Skips separators
+ */
+void
+MAGReader::skip_sep ()
+{
+ while (! m_stream.at_end ()) {
+ char c = m_stream.peek_char ();
+ if (isdigit (c) || c == '-' || c == '(' || c == ')' || c == ';') {
+ return;
+ }
+ m_stream.get_char ();
+ }
+}
+
+/**
+ * @brief Skip comments
+ * This assumes that the reader is after the first '(' and it will stop
+ * after the final ')'.
+ */
+void
+MAGReader::skip_comment ()
+{
+ char c;
+ int bl = 0;
+ while (! m_stream.at_end () && ((c = m_stream.get_char ()) != ')' || bl > 0)) {
+ // check for nested comments (bl is the nesting level)
+ if (c == '(') {
+ ++bl;
+ } else if (c == ')') {
+ --bl;
+ }
+ }
+}
+
+/**
+ * @brief Gets a character and issues an error if the stream is at the end
+ */
+char
+MAGReader::get_char ()
+{
+ if (m_stream.at_end ()) {
+ error ("Unexpected end of file");
+ return 0;
+ } else {
+ return m_stream.get_char ();
+ }
+}
+
+/**
+ * @brief Tests whether the next character is a semicolon (after blanks)
+ */
+bool
+MAGReader::test_semi ()
+{
+ skip_blanks ();
+ if (! m_stream.at_end () && m_stream.peek_char () == ';') {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+/**
+ * @brief Tests whether a semicolon follows and issue an error if not
+ */
+void
+MAGReader::expect_semi ()
+{
+ if (! test_semi ()) {
+ error ("Expected ';' command terminator");
+ } else {
+ get_char ();
+ }
+}
+
+/**
+ * @brief Skips all until the next semicolon
+ */
+void
+MAGReader::skip_to_end ()
+{
+ while (! m_stream.at_end () && m_stream.get_char () != ';') {
+ ;
+ }
+}
+
+/**
+ * @brief Fetches an integer
+ */
+int
+MAGReader::read_integer_digits ()
+{
+ if (m_stream.at_end () || ! isdigit (m_stream.peek_char ())) {
+ error ("Digit expected");
+ }
+
+ int i = 0;
+ while (! m_stream.at_end () && isdigit (m_stream.peek_char ())) {
+
+ if (i > std::numeric_limits::max () / 10) {
+
+ error ("Integer overflow");
+ while (! m_stream.at_end () && isdigit (m_stream.peek_char ())) {
+ m_stream.get_char ();
+ }
+
+ return 0;
+
+ }
+
+ char c = m_stream.get_char ();
+ i = i * 10 + int (c - '0');
+
+ }
+
+ return i;
+}
+
+/**
+ * @brief Fetches an integer
+ */
+int
+MAGReader::read_integer ()
+{
+ skip_sep ();
+ return read_integer_digits ();
+}
+
+/**
+ * @brief Fetches a signed integer
+ */
+int
+MAGReader::read_sinteger ()
+{
+ skip_sep ();
+
+ bool neg = false;
+ if (m_stream.peek_char () == '-') {
+ m_stream.get_char ();
+ neg = true;
+ }
+
+ int i = read_integer_digits ();
+ return neg ? -i : i;
+}
+
+/**
+ * @brief Fetches a string (layer name)
+ */
+const std::string &
+MAGReader::read_name ()
+{
+ skip_blanks ();
+
+ m_cmd_buffer.clear ();
+ if (m_stream.at_end ()) {
+ return m_cmd_buffer;
+ }
+
+ // Note: officially only upper and digits are allowed in names. But we allow lower case and "_" too ...
+ while (! m_stream.at_end () && (isupper (m_stream.peek_char ()) || islower (m_stream.peek_char ()) || m_stream.peek_char () == '_' || isdigit (m_stream.peek_char ()))) {
+ m_cmd_buffer += m_stream.get_char ();
+ }
+
+ return m_cmd_buffer;
+}
+
+/**
+ * @brief Fetches a string (in labels, texts)
+ */
+const std::string &
+MAGReader::read_string ()
+{
+ m_stream.skip ();
+
+ m_cmd_buffer.clear ();
+ if (m_stream.at_end ()) {
+ return m_cmd_buffer;
+ }
+
+ char q = m_stream.peek_char ();
+ if (q == '"' || q == '\'') {
+
+ get_char ();
+
+ // read a quoted string (KLayout extension)
+ while (! m_stream.at_end () && m_stream.peek_char () != q) {
+ char c = m_stream.get_char ();
+ if (c == '\\' && ! m_stream.at_end ()) {
+ c = m_stream.get_char ();
+ }
+ m_cmd_buffer += c;
+ }
+
+ if (! m_stream.at_end ()) {
+ get_char ();
+ }
+
+ } else {
+
+ while (! m_stream.at_end () && !isspace (m_stream.peek_char ()) && m_stream.peek_char () != ';') {
+ m_cmd_buffer += m_stream.get_char ();
+ }
+
+ }
+
+ return m_cmd_buffer;
+}
+
+/**
+ * @brief Reads a double value (extension)
+ */
+double
+MAGReader::read_double ()
+{
+ m_stream.skip ();
+
+ // read a quoted string (KLayout extension)
+ m_cmd_buffer.clear ();
+ while (! m_stream.at_end () && (isdigit (m_stream.peek_char ()) || m_stream.peek_char () == '.' || m_stream.peek_char () == '-' || m_stream.peek_char () == 'e' || m_stream.peek_char () == 'E')) {
+ m_cmd_buffer += m_stream.get_char ();
+ }
+
+ double v = 0.0;
+ tl::from_string (m_cmd_buffer, v);
+ return v;
+}
+
+bool
+MAGReader::read_cell (db::Layout &layout, db::Cell &cell, double sf, int level)
+{
+ if (fabs (sf - floor (sf + 0.5)) > 1e-6) {
+ warn ("Scaling factor is not an integer - snapping errors may occur in cell '" + m_cellname + "'");
+ }
+
+ int nx = 0, ny = 0, dx = 0, dy = 0;
+ int layer = -2; // no layer defined yet.
+ int path_mode = -1;
+ size_t insts = 0;
+ size_t shapes = 0;
+ size_t layer_specs = 0;
+ std::vector poly_pts;
+
+ while (true) {
+
+ skip_blanks ();
+
+ char c = get_char ();
+ if (c == ';') {
+
+ // empty command
+
+ } else if (c == '(') {
+
+ skip_comment ();
+
+ } else if (c == 'E') {
+
+ if (level > 0) {
+ error ("'E' command must be outside a cell specification");
+ }
+
+ skip_blanks ();
+ break;
+
+ } else if (c == 'D') {
+
+ skip_blanks ();
+
+ c = get_char ();
+ if (c == 'S') {
+
+ // DS command:
+ // "D" blank* "S" integer (sep integer sep integer)?
+
+ unsigned int n = 0, denom = 1, divider = 1;
+ n = read_integer ();
+ if (! test_semi ()) {
+ denom = read_integer ();
+ divider = read_integer ();
+ if (divider == 0) {
+ error ("'DS' command: divider cannot be zero");
+ }
+ }
+
+ expect_semi ();
+
+ std::string outer_cell = "C" + tl::to_string (n);
+ std::swap (m_cellname, outer_cell);
+
+ std::map ::const_iterator c = m_cells_by_id.find (n);
+ db::cell_index_type ci;
+ if (c == m_cells_by_id.end ()) {
+ ci = layout.add_cell (m_cellname.c_str ());
+ m_cells_by_id.insert (std::make_pair (n, ci));
+ } else {
+ ci = c->second;
+ }
+
+ db::Cell &cell = layout.cell (ci);
+
+ read_cell (layout, cell, sf * double (denom) / double (divider), level + 1);
+
+ std::swap (m_cellname, outer_cell);
+
+ } else if (c == 'F') {
+
+ // DF command:
+ // "D" blank* "F"
+ if (level == 0) {
+ error ("'DF' command must be inside a cell specification");
+ }
+
+ // skip the rest of the command
+ skip_to_end ();
+
+ break;
+
+ } else if (c == 'D') {
+
+ // DD command:
+ // "D" blank* "D" integer
+
+ read_integer ();
+ warn ("'DD' command ignored");
+ skip_to_end ();
+
+ } else {
+
+ error ("Invalid 'D' sub-command");
+ skip_to_end ();
+
+ }
+
+ } else if (c == 'C') {
+
+ // C command:
+ // "C" integer transformation
+ // transformation := (blank* ("T" point |"M" blank* "X" |"M" blank* "Y" |"R" point)*)*
+
+ ++insts;
+
+ unsigned int n = read_integer ();
+ std::map ::const_iterator c = m_cells_by_id.find (n);
+ if (c == m_cells_by_id.end ()) {
+ std::string cn = "C" + tl::to_string (n);
+ c = m_cells_by_id.insert (std::make_pair (n, layout.add_cell (cn.c_str ()))).first;
+ }
+
+ db::DCplxTrans trans;
+
+ while (! test_semi ()) {
+
+ skip_blanks ();
+
+ char ct = get_char ();
+ if (ct == 'M') {
+
+ skip_blanks ();
+
+ char ct2 = get_char ();
+ if (ct2 == 'X') {
+ trans = db::DCplxTrans (db::FTrans::m90) * trans;
+ } else if (ct2 == 'Y') {
+ trans = db::DCplxTrans (db::FTrans::m0) * trans;
+ } else {
+ error ("Invalid 'M' transformation specification");
+ // skip everything to avoid more errors here
+ while (! test_semi ()) {
+ get_char ();
+ }
+ }
+
+ } else if (ct == 'T') {
+
+ int x = read_sinteger ();
+ int y = read_sinteger ();
+ trans = db::DCplxTrans (db::DVector (x * sf, y * sf)) * trans;
+
+ } else if (ct == 'R') {
+
+ int x = read_sinteger ();
+ int y = read_sinteger ();
+
+ if (y != 0 || x != 0) {
+ double a = atan2 (double (y), double (x)) * 180.0 / M_PI;
+ trans = db::DCplxTrans (1.0, a, false, db::DVector ()) * trans;
+ }
+
+ } else {
+ error ("Invalid transformation specification");
+ // skip everything to avoid more errors here
+ while (! test_semi ()) {
+ get_char ();
+ }
+ }
+
+ }
+
+ if (nx > 0 || ny > 0) {
+ if (trans.is_ortho () && ! trans.is_mag ()) {
+ cell.insert (db::CellInstArray (db::CellInst (c->second), db::Trans (db::ICplxTrans (trans)), db::Vector (dx * sf, 0.0), db::Vector (0.0, dy * sf), std::max (1, nx), std::max (1, ny)));
+ } else {
+ cell.insert (db::CellInstArray (db::CellInst (c->second), db::ICplxTrans (trans), db::Vector (dx * sf, 0.0), db::Vector (0.0, dy * sf), std::max (1, nx), std::max (1, ny)));
+ }
+ } else {
+ if (trans.is_ortho () && ! trans.is_mag ()) {
+ cell.insert (db::CellInstArray (db::CellInst (c->second), db::Trans (db::ICplxTrans (trans))));
+ } else {
+ cell.insert (db::CellInstArray (db::CellInst (c->second), db::ICplxTrans (trans)));
+ }
+ }
+
+ nx = ny = 0;
+ dx = dy = 0;
+
+ expect_semi ();
+
+ } else if (c == 'L') {
+
+ skip_blanks ();
+
+ ++layer_specs;
+
+ std::string name = read_name ();
+ if (name.empty ()) {
+ error ("Missing layer name in 'L' command");
+ }
+
+ std::pair ll = open_layer (layout, name);
+ if (! ll.first) {
+ layer = -1; // ignore geometric objects on this layer
+ } else {
+ layer = int (ll.second);
+ }
+
+ expect_semi ();
+
+ } else if (c == 'B') {
+
+ ++shapes;
+
+ if (layer < 0) {
+
+ if (layer < -1) {
+ warn ("'B' command ignored since no layer was selected");
+ }
+ skip_to_end ();
+
+ } else {
+
+ unsigned int w = 0, h = 0;
+ int x = 0, y = 0;
+
+ w = read_integer ();
+ h = read_integer ();
+ x = read_sinteger ();
+ y = read_sinteger ();
+
+ int rx = 0, ry = 0;
+ if (! test_semi ()) {
+ rx = read_sinteger ();
+ ry = read_sinteger ();
+ }
+
+ if (rx >= 0 && ry == 0) {
+
+ cell.shapes ((unsigned int) layer).insert (db::Box (sf * (x - 0.5 * w), sf * (y - 0.5 * h), sf * (x + 0.5 * w), sf * (y + 0.5 * h)));
+
+ } else {
+
+ double n = 1.0 / sqrt (double (rx) * double (rx) + double (ry) * double (ry));
+
+ double xw = sf * w * 0.5 * rx * n, yw = sf * w * 0.5 * ry * n;
+ double xh = -sf * h * 0.5 * ry * n, yh = sf * h * 0.5 * rx * n;
+
+ db::Point c (sf * x, sf * y);
+
+ db::Point points [4];
+ points [0] = c + db::Vector (-xw - xh, -yw - yh);
+ points [1] = c + db::Vector (-xw + xh, -yw + yh);
+ points [2] = c + db::Vector (xw + xh, yw + yh);
+ points [3] = c + db::Vector (xw - xh, yw - yh);
+
+ db::Polygon p;
+ p.assign_hull (points, points + 4);
+ cell.shapes ((unsigned int) layer).insert (p);
+
+ }
+
+ expect_semi ();
+
+ }
+
+ } else if (c == 'P') {
+
+ ++shapes;
+
+ if (layer < 0) {
+
+ if (layer < -1) {
+ warn ("'P' command ignored since no layer was selected");
+ }
+ skip_to_end ();
+
+ } else {
+
+ poly_pts.clear ();
+
+ while (! test_semi ()) {
+
+ int rx = read_sinteger ();
+ int ry = read_sinteger ();
+
+ poly_pts.push_back (db::Point (sf * rx, sf * ry));
+
+ }
+
+ db::Polygon p;
+ p.assign_hull (poly_pts.begin (), poly_pts.end ());
+ cell.shapes ((unsigned int) layer).insert (p);
+
+ expect_semi ();
+
+ }
+
+ } else if (c == 'R') {
+
+ ++shapes;
+
+ if (layer < 0) {
+
+ if (layer < -1) {
+ warn ("'R' command ignored since no layer was selected");
+ }
+ skip_to_end ();
+
+ } else {
+
+ int w = read_integer ();
+
+ poly_pts.clear ();
+
+ int rx = read_sinteger ();
+ int ry = read_sinteger ();
+
+ poly_pts.push_back (db::Point (sf * rx, sf * ry));
+
+ db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), db::coord_traits ::rounded (sf * w / 2), db::coord_traits ::rounded (sf * w / 2), true);
+ cell.shapes ((unsigned int) layer).insert (p);
+
+ expect_semi ();
+
+ }
+
+ } else if (c == 'W') {
+
+ ++shapes;
+
+ if (layer < 0) {
+
+ if (layer < -1) {
+ warn ("'W' command ignored since no layer was selected");
+ }
+
+ skip_to_end ();
+
+ } else {
+
+ int w = read_integer ();
+
+ poly_pts.clear ();
+
+ while (! test_semi ()) {
+
+ int rx = read_sinteger ();
+ int ry = read_sinteger ();
+
+ poly_pts.push_back (db::Point (sf * rx, sf * ry));
+
+ }
+
+ if (path_mode == 0 || (path_mode < 0 && m_wire_mode == 1)) {
+ // Flush-ended paths
+ db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), 0, 0, false);
+ cell.shapes ((unsigned int) layer).insert (p);
+ } else if (path_mode == 1 || (path_mode < 0 && m_wire_mode == 2)) {
+ // Round-ended paths
+ db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), db::coord_traits ::rounded (sf * w / 2), db::coord_traits ::rounded (sf * w / 2), true);
+ cell.shapes ((unsigned int) layer).insert (p);
+ } else {
+ // Square-ended paths
+ db::Path p (poly_pts.begin (), poly_pts.end (), db::coord_traits ::rounded (sf * w), db::coord_traits ::rounded (sf * w / 2), db::coord_traits ::rounded (sf * w / 2), false);
+ cell.shapes ((unsigned int) layer).insert (p);
+ }
+
+ expect_semi ();
+
+ }
+
+ } else if (isdigit (c)) {
+
+ char cc = m_stream.peek_char ();
+ if (c == '9' && cc == '3') {
+
+ get_char ();
+
+ nx = read_sinteger ();
+ dx = read_sinteger ();
+ ny = read_sinteger ();
+ dy = read_sinteger ();
+
+ } else if (c == '9' && cc == '4') {
+
+ get_char ();
+
+ // label at location
+ ++shapes;
+
+ if (layer < 0) {
+ if (layer < -1) {
+ warn ("'94' command ignored since no layer was selected");
+ }
+ } else {
+
+ std::string text = read_string ();
+
+ int rx = read_sinteger ();
+ int ry = read_sinteger ();
+
+ double h = 0.0;
+ if (! test_semi ()) {
+ h = read_double ();
+ }
+
+ db::Text t (text.c_str (), db::Trans (db::Vector (sf * rx, sf * ry)), db::coord_traits ::rounded (h / m_dbu));
+ cell.shapes ((unsigned int) layer).insert (t);
+
+ }
+
+ } else if (c == '9' && cc == '5') {
+
+ get_char ();
+
+ // label in box
+ ++shapes;
+
+ if (layer < 0) {
+ if (layer < -1) {
+ warn ("'95' command ignored since no layer was selected");
+ }
+ } else {
+
+ std::string text = read_string ();
+
+ // TODO: box dimensions are ignored currently.
+ read_sinteger ();
+ read_sinteger ();
+
+ int rx = read_sinteger ();
+ int ry = read_sinteger ();
+
+ db::Text t (text.c_str (), db::Trans (db::Vector (sf * rx, sf * ry)));
+ cell.shapes ((unsigned int) layer).insert (t);
+
+ }
+
+ } else if (c == '9' && cc == '8') {
+
+ get_char ();
+
+ // Path type (0: flush, 1: round, 2: square)
+ path_mode = read_integer ();
+
+ } else if (c == '9' && ! isdigit (cc)) {
+
+ m_cellname = read_string ();
+ m_cellname = layout.uniquify_cell_name (m_cellname.c_str ());
+ layout.rename_cell (cell.cell_index (), m_cellname.c_str ());
+
+ } else {
+ // ignore the command
+ }
+
+ skip_to_end ();
+
+ } else {
+
+ // ignore the command
+ warn ("Unknown command ignored");
+ skip_to_end ();
+
+ }
+
+ }
+
+ // The cell is considered non-empty if it contains more than one instance, at least one shape or
+ // has at least one "L" command.
+ return insts > 1 || shapes > 0 || layer_specs > 0;
+}
+#endif
+
+void
+MAGReader::do_read (db::Layout &layout)
+{
+#if 0 // @@@
+ tl::SelfTimer timer (tl::verbosity () >= 21, "File read");
+
+ try {
+
+ double sf = 0.01 / m_dbu;
+ layout.dbu (m_dbu);
+
+ m_cellname = "{MAG top level}";
+
+ db::Cell &cell = layout.cell (layout.add_cell ());
+
+ if (! read_cell (layout, cell, sf, 0)) {
+ // The top cell is empty or contains a single instance: discard it.
+ layout.delete_cell (cell.cell_index ());
+ } else {
+ layout.rename_cell (cell.cell_index (), layout.uniquify_cell_name ("MAG_TOP").c_str ());
+ }
+
+ m_cellname = std::string ();
+
+ skip_blanks ();
+
+ if (! m_stream.at_end ()) {
+ warn ("E command is followed by more text");
+ }
+
+ } catch (db::MAGReaderException &ex) {
+ throw ex;
+ } catch (tl::Exception &ex) {
+ error (ex.msg ());
+ }
+#endif
+}
+
+}
+
diff --git a/src/plugins/streamers/magic/db_plugin/dbMAGReader.h b/src/plugins/streamers/magic/db_plugin/dbMAGReader.h
new file mode 100644
index 000000000..1226218ac
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.h
@@ -0,0 +1,146 @@
+
+/*
+
+ KLayout Layout Viewer
+ Copyright (C) 2006-2019 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
+
+*/
+
+
+
+#ifndef HDR_dbMAGReader
+#define HDR_dbMAGReader
+
+#include "dbPluginCommon.h"
+#include "dbNamedLayerReader.h"
+#include "dbLayout.h"
+#include "dbMAG.h"
+#include "dbMAGFormat.h"
+#include "dbStreamLayers.h"
+#include "dbPropertiesRepository.h"
+
+#include "tlException.h"
+#include "tlInternational.h"
+#include "tlProgress.h"
+#include "tlString.h"
+#include "tlStream.h"
+
+#include