diff --git a/src/buddies/src/bd/bd.pro b/src/buddies/src/bd/bd.pro
index 3ec14b734..ef71f239e 100644
--- a/src/buddies/src/bd/bd.pro
+++ b/src/buddies/src/bd/bd.pro
@@ -21,6 +21,7 @@ SOURCES = \
strmcmp.cc \
strmxor.cc \
strmrun.cc \
+ strm2mag.cc
HEADERS = \
bdCommon.h \
diff --git a/src/buddies/src/bd/bdReaderOptions.cc b/src/buddies/src/bd/bdReaderOptions.cc
index c7fc839d8..186b68655 100644
--- a/src/buddies/src/bd/bdReaderOptions.cc
+++ b/src/buddies/src/bd/bdReaderOptions.cc
@@ -48,7 +48,11 @@ GenericReaderOptions::GenericReaderOptions ()
m_dxf_contour_accuracy (0.0),
m_dxf_render_texts_as_polygons (false),
m_dxf_keep_layer_names (false),
- m_dxf_keep_other_cells (false)
+ m_dxf_keep_other_cells (false),
+ m_magic_lambda (1.0),
+ m_magic_dbu (0.001),
+ m_magic_keep_layer_names (false),
+ m_magic_merge (true)
{
// .. nothing yet ..
}
@@ -238,6 +242,27 @@ GenericReaderOptions::add_options (tl::CommandLineOptions &cmd)
)
;
}
+
+ {
+ std::string group ("[" + m_group_prefix + " options - MAG (Magic) specific]");
+
+ cmd << tl::arg (group +
+ "--" + m_long_prefix + "magic-lambda=lambda", &m_magic_lambda, "Specifies the lambda value",
+ "The lambda value is used as a scaling factor to turn the dimensionless Magic drawings into "
+ "physical layout."
+ )
+ << tl::arg (group +
+ "#!--" + m_long_prefix + "magic-dont-merge", &m_magic_merge, "Disables polygon merging",
+ "With this option, the rectangles and triangles of the Magic file are not merged into polygons."
+ )
+ << tl::arg (group +
+ "--" + m_long_prefix + "magic-lib-path=path", &m_magic_lib_path, "Specifies the library search path for Magic file loading",
+ "The library search path gives the locations where the reader looks up files for child cells. "
+ "This option either specifies a comma-separated list of paths to search or it can be present multiple times "
+ "for multiple search locations."
+ )
+ ;
+ }
}
void GenericReaderOptions::set_layer_map (const std::string &lm)
@@ -256,12 +281,14 @@ void GenericReaderOptions::set_read_named_layers (bool f)
{
m_dxf_keep_layer_names = f;
m_cif_keep_layer_names = f;
+ m_magic_keep_layer_names = f;
}
void GenericReaderOptions::set_dbu (double dbu)
{
m_dxf_dbu = dbu;
m_cif_dbu = dbu;
+ m_magic_dbu = dbu;
}
void
@@ -297,6 +324,14 @@ GenericReaderOptions::configure (db::LoadLayoutOptions &load_options) const
load_options.set_option_by_name ("dxf_render_texts_as_polygons", m_dxf_render_texts_as_polygons);
load_options.set_option_by_name ("dxf_keep_layer_names", m_dxf_keep_layer_names);
load_options.set_option_by_name ("dxf_keep_other_cells", m_dxf_keep_other_cells);
+
+ load_options.set_option_by_name ("mag_layer_map", tl::Variant::make_variant (m_layer_map));
+ load_options.set_option_by_name ("mag_create_other_layers", m_create_other_layers);
+ load_options.set_option_by_name ("mag_dbu", m_magic_dbu);
+ load_options.set_option_by_name ("mag_lambda", m_magic_lambda);
+ load_options.set_option_by_name ("mag_merge", m_magic_merge);
+ load_options.set_option_by_name ("mag_keep_layer_names", m_magic_keep_layer_names);
+ load_options.set_option_by_name ("mag_library_paths", tl::Variant (m_magic_lib_path.begin (), m_magic_lib_path.end ()));
}
}
diff --git a/src/buddies/src/bd/bdReaderOptions.h b/src/buddies/src/bd/bdReaderOptions.h
index 7158d0552..03982b53c 100644
--- a/src/buddies/src/bd/bdReaderOptions.h
+++ b/src/buddies/src/bd/bdReaderOptions.h
@@ -130,6 +130,13 @@ private:
bool m_dxf_keep_layer_names;
bool m_dxf_keep_other_cells;
+ // MAGIC
+ double m_magic_lambda;
+ double m_magic_dbu;
+ bool m_magic_keep_layer_names;
+ bool m_magic_merge;
+ std::vector m_magic_lib_path;
+
void set_layer_map (const std::string &lm);
void set_dbu (double dbu);
void set_read_named_layers (bool f);
diff --git a/src/buddies/src/bd/bdWriterOptions.cc b/src/buddies/src/bd/bdWriterOptions.cc
index 7605c389f..60b72e737 100644
--- a/src/buddies/src/bd/bdWriterOptions.cc
+++ b/src/buddies/src/bd/bdWriterOptions.cc
@@ -53,6 +53,7 @@ GenericWriterOptions::GenericWriterOptions ()
m_oasis_subst_char ("*"),
m_cif_dummy_calls (false),
m_cif_blank_separator (false),
+ m_magic_lambda (1.0),
m_dxf_polygon_mode (0)
{
// .. nothing yet ..
@@ -63,6 +64,7 @@ const std::string GenericWriterOptions::gds2text_format_name = "GDS2Text"; //
const std::string GenericWriterOptions::oasis_format_name = "OASIS";
const std::string GenericWriterOptions::dxf_format_name = "DXF";
const std::string GenericWriterOptions::cif_format_name = "CIF";
+const std::string GenericWriterOptions::mag_format_name = "MAG";
void
GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::string &format)
@@ -267,6 +269,21 @@ GenericWriterOptions::add_options (tl::CommandLineOptions &cmd, const std::strin
;
}
+
+ if (format.empty () || format == mag_format_name) {
+
+ // Add MAG format options
+ std::string group = "[Output options - MAG (Magic) specific]";
+
+ cmd << tl::arg (group +
+ "--magic-lambda-out=lambda", &m_magic_lambda, "Specifies the lambda value when writing Magic files (which are unitless)"
+ )
+ << tl::arg (group +
+ "--magic-tech", &m_magic_tech, "Specifies the technology to include in the Magic files"
+ )
+ ;
+
+ }
}
void GenericWriterOptions::set_oasis_substitution_char (const std::string &text)
@@ -353,6 +370,9 @@ GenericWriterOptions::configure (db::SaveLayoutOptions &save_options, const db::
save_options.set_option_by_name ("dxf_polygon_mode", m_dxf_polygon_mode);
+ save_options.set_option_by_name ("mag_lambda", m_magic_lambda);
+ save_options.set_option_by_name ("mag_tech", m_magic_tech);
+
if (!m_cell_selection.empty ()) {
std::set selected;
diff --git a/src/buddies/src/bd/bdWriterOptions.h b/src/buddies/src/bd/bdWriterOptions.h
index 197b073c9..4246c27d1 100644
--- a/src/buddies/src/bd/bdWriterOptions.h
+++ b/src/buddies/src/bd/bdWriterOptions.h
@@ -103,6 +103,7 @@ public:
static const std::string oasis_format_name;
static const std::string cif_format_name;
static const std::string dxf_format_name;
+ static const std::string mag_format_name;
private:
double m_scale_factor;
@@ -133,6 +134,9 @@ private:
bool m_cif_dummy_calls;
bool m_cif_blank_separator;
+ double m_magic_lambda;
+ std::string m_magic_tech;
+
int m_dxf_polygon_mode;
void set_oasis_substitution_char (const std::string &text);
diff --git a/src/buddies/src/bd/strm2mag.cc b/src/buddies/src/bd/strm2mag.cc
new file mode 100644
index 000000000..38207870a
--- /dev/null
+++ b/src/buddies/src/bd/strm2mag.cc
@@ -0,0 +1,29 @@
+
+/*
+
+ 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 "bdConverterMain.h"
+#include "bdWriterOptions.h"
+
+BD_PUBLIC int strm2mag (int argc, char *argv[])
+{
+ return bd::converter_main (argc, argv, bd::GenericWriterOptions::mag_format_name);
+}
diff --git a/src/buddies/src/src.pro b/src/buddies/src/src.pro
index 3cf2321ba..eb9777725 100644
--- a/src/buddies/src/src.pro
+++ b/src/buddies/src/src.pro
@@ -8,6 +8,7 @@ SUBDIRS = \
strm2gds \
strm2gdstxt \
strm2oas \
+ strm2mag \
strm2txt \
strmclip \
strmcmp \
@@ -19,6 +20,7 @@ strm2dxf.depends += bd
strm2gds.depends += bd
strm2gdstxt.depends += bd
strm2oas.depends += bd
+strm2mag.depends += bd
strm2txt.depends += bd
strmclip.depends += bd
strmcmp.depends += bd
diff --git a/src/buddies/src/strm2mag/strm2mag.pro b/src/buddies/src/strm2mag/strm2mag.pro
new file mode 100644
index 000000000..e04ca961a
--- /dev/null
+++ b/src/buddies/src/strm2mag/strm2mag.pro
@@ -0,0 +1,2 @@
+
+include($$PWD/../buddy_app.pri)
diff --git a/src/buddies/unit_tests/bdConverterTests.cc b/src/buddies/unit_tests/bdConverterTests.cc
index 739d40b13..7a57a2467 100644
--- a/src/buddies/unit_tests/bdConverterTests.cc
+++ b/src/buddies/unit_tests/bdConverterTests.cc
@@ -158,3 +158,31 @@ TEST(5)
db::compare_layouts (this, layout, input, db::NoNormalization);
}
+// Testing the converter main implementation (MAG)
+TEST(6)
+{
+ std::string input = tl::testsrc ();
+ input += "/testdata/gds/t10.gds";
+
+ std::string input_au = tl::testsrc ();
+ input_au += "/testdata/magic/strm2mag_au.gds";
+
+ std::string output = this->tmp_file ("RINGO.mag");
+
+ const char *argv[] = { "x", input.c_str (), output.c_str (), "--magic-lambda-out=0.005" };
+
+ EXPECT_EQ (bd::converter_main (sizeof (argv) / sizeof (argv[0]), (char **) argv, bd::GenericWriterOptions::mag_format_name), 0);
+
+ db::Layout layout;
+
+ {
+ tl::InputStream stream (output);
+ db::LoadLayoutOptions options;
+ options.set_option_by_name ("mag_lambda", 0.005);
+ db::Reader reader (stream);
+ reader.read (layout, options);
+ EXPECT_EQ (reader.format (), "MAG");
+ }
+
+ db::compare_layouts (this, layout, input_au, db::WriteGDS2);
+}
diff --git a/src/db/db/dbNamedLayerReader.cc b/src/db/db/dbNamedLayerReader.cc
index 43975c685..2bc9c7a1f 100644
--- a/src/db/db/dbNamedLayerReader.cc
+++ b/src/db/db/dbNamedLayerReader.cc
@@ -84,7 +84,7 @@ extract_ld (const char *s, int &l, int &d, std::string &n)
{
l = d = 0;
- if (*s == 'L') {
+ if (*s == 'L' || *s == 'l') {
++s;
}
@@ -97,7 +97,7 @@ extract_ld (const char *s, int &l, int &d, std::string &n)
++s;
}
- if (*s == 'D' || *s == '.') {
+ if (*s == 'D' || *s == 'd' || *s == '.') {
++s;
if (! safe_isdigit (*s)) {
return false;
diff --git a/src/db/db/dbSaveLayoutOptions.cc b/src/db/db/dbSaveLayoutOptions.cc
index 5dd4e5c8e..7c5cc2bfc 100644
--- a/src/db/db/dbSaveLayoutOptions.cc
+++ b/src/db/db/dbSaveLayoutOptions.cc
@@ -294,6 +294,17 @@ SaveLayoutOptions::get_valid_layers (const db::Layout &layout, std::vector >::const_iterator l = all_layers.begin (); l != all_layers.end (); ++l) {
+ layers.push_back (*l);
+ if (l->second.name.empty ()) {
+ layers.back ().second = tl::sprintf ("L%dD%d", l->second.layer, l->second.datatype);
+ } else if (l->second.layer >= 0 && l->second.datatype >= 0) {
+ layers.back ().second = l->second.name;
+ }
+ }
+
} else if (lm == LP_AssignNumber) {
int next_layer = 0;
diff --git a/src/db/db/dbSaveLayoutOptions.h b/src/db/db/dbSaveLayoutOptions.h
index b6cc74d33..7de5facd4 100644
--- a/src/db/db/dbSaveLayoutOptions.h
+++ b/src/db/db/dbSaveLayoutOptions.h
@@ -391,7 +391,8 @@ public:
LP_OnlyNumbered = 0,
LP_OnlyNamed = 1,
LP_AssignName = 2,
- LP_AssignNumber = 3
+ LP_AssignNameWithPriority = 3,
+ LP_AssignNumber = 4
};
/**
@@ -401,7 +402,8 @@ public:
* The lm mode specifies how to create layer properties for "halfway defined" layers -
* - LP_OnlyNamed will only select named ones
* - LP_OnlyNumbered will select only numbered ones
- * - LP_AssignName will assign a name when no name is given
+ * - LP_AssignName will assign a name when no name is given plus encode layer/datatype when given
+ * - LP_AssignNameWithPriority will assign a name when no name is given and does not encore layer/datatype together with a name
* - LP_AssignNumber will assign numbers when no number is given
*/
void get_valid_layers (const db::Layout &layout, std::vector > &valid_layers, LayerAssignmentMode lm) const;
diff --git a/src/db/db/dbShapeProcessor.h b/src/db/db/dbShapeProcessor.h
index 631fdf02b..ebb88aefb 100644
--- a/src/db/db/dbShapeProcessor.h
+++ b/src/db/db/dbShapeProcessor.h
@@ -160,6 +160,14 @@ public:
*/
void reserve (size_t n);
+ /**
+ * @brief Sets the base verbosity of the processor (see EdgeProcessor::set_base_verbosity for details)
+ */
+ void set_base_verbosity (int bv)
+ {
+ m_processor.set_base_verbosity (bv);
+ }
+
/**
* @brief Enable progress
*
diff --git a/src/db/db/dbText.h b/src/db/db/dbText.h
index f69000a1f..61b787ce2 100644
--- a/src/db/db/dbText.h
+++ b/src/db/db/dbText.h
@@ -676,11 +676,11 @@ public:
typedef typename Tr::target_coord_type target_coord_type;
size_t p = (size_t) mp_ptr;
if (p & 1) {
- return text (reinterpret_cast (p - 1), simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font);
+ return text (reinterpret_cast (p - 1), simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign);
} else if (mp_ptr) {
- return text (mp_ptr, simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font);
+ return text (mp_ptr, simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign);
} else {
- return text (simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font);
+ return text (simple_trans ((t.fp_trans () * m_trans.fp_trans ()).rot (), t (point_type () + m_trans.disp ()) - point ()), t.ctrans (m_size), m_font, m_halign, m_valign);
}
}
diff --git a/src/db/db/gsiDeclDbCommonStreamOptions.cc b/src/db/db/gsiDeclDbCommonStreamOptions.cc
index ad56d81cd..feeb5588b 100644
--- a/src/db/db/gsiDeclDbCommonStreamOptions.cc
+++ b/src/db/db/gsiDeclDbCommonStreamOptions.cc
@@ -89,15 +89,15 @@ static
gsi::ClassExt common_reader_options (
gsi::method_ext ("set_layer_map", &set_layer_map, gsi::arg ("map"), gsi::arg ("create_other_layers"),
"@brief Sets the layer map\n"
- "This sets a layer mapping for the reader. The \"create_other_layers\" specifies whether to create layers that are not "
- "in the mapping and automatically assign layers to them.\n"
+ "This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers, for example to add a layer name.\n"
"@param map The layer map to set."
- "@param create_other_layers The flag telling whether other layer should be created also. Set to false if just the layers in the mapping table should be read.\n"
+ "@param create_other_layers The flag telling whether other layer should be created as well. Set to false if just the layers in the mapping table should be read.\n"
"\n"
"Starting with version 0.25 this option only applies to GDS2 and OASIS format. Other formats provide their own configuration."
) +
gsi::method_ext ("layer_map=", &set_layer_map1, gsi::arg ("map"),
"@brief Sets the layer map, but does not affect the \"create_other_layers\" flag.\n"
+ "Use \\create_other_layers? to enable or disable other layers not listed in the layer map.\n"
"@param map The layer map to set."
"\n"
"This convenience method has been introduced with version 0.26."
@@ -121,12 +121,15 @@ gsi::ClassExt common_reader_options (
gsi::method_ext ("create_other_layers?", &create_other_layers,
"@brief Gets a value indicating whether other layers shall be created\n"
"@return True, if other layers should be created.\n"
+ "This attribute acts together with a layer map (see \\layer_map=). Layers not listed in this map are created as well when "
+ "\\create_other_layers? is true. Otherwise they are ignored.\n"
"\n"
"Starting with version 0.25 this option only applies to GDS2 and OASIS format. Other formats provide their own configuration."
) +
gsi::method_ext ("create_other_layers=", &set_create_other_layers, gsi::arg ("create"),
"@brief Specifies whether other layers shall be created\n"
"@param create True, if other layers should be created.\n"
+ "See \\create_other_layers? for a description of this attribute.\n"
"\n"
"Starting with version 0.25 this option only applies to GDS2 and OASIS format. Other formats provide their own configuration."
) +
diff --git a/src/drc/drc/built-in-macros/_drc_engine.rb b/src/drc/drc/built-in-macros/_drc_engine.rb
index 1d5879673..7b89a65eb 100644
--- a/src/drc/drc/built-in-macros/_drc_engine.rb
+++ b/src/drc/drc/built-in-macros/_drc_engine.rb
@@ -894,8 +894,9 @@ module DRC
# "what" specifies what input to use. "what" be either
#
# @ul
- # @li A string "\@n" specifying output to a layout in the current panel @/li
- # @li A layout filename @/li
+ # @li A string "\@n" (n is an integer) specifying output to a layout in the current panel @/li
+ # @li A string "\@+" specifying output to a new layout in the current panel @/li
+ # @li A layout filename @/li
# @li A RBA::Layout object @/li
# @li A RBA::Cell object @/li
# @/ul
@@ -912,15 +913,21 @@ module DRC
if arg.is_a?(String)
- if arg =~ /^@(\d+)/
- n = $1.to_i - 1
+ if arg =~ /^@(\d+|\+)/
view = RBA::LayoutView::current
view || raise("No view open")
+ if $1 == "+"
+ n = view.create_layout(true)
+ cellname ||= (@def_cell ? @def_cell.name : "TOP")
+ else
+ n = $1.to_i - 1
+ end
(n >= 0 && view.cellviews > n) || raise("Invalid layout index @#{n + 1}")
cv = view.cellview(n)
cv.is_valid? || raise("Invalid layout @#{n + 1}")
@output_layout = cv.layout
@output_cell = cellname ? (@output_layout.cell(cellname.to_s) || @output_layout.create_cell(cellname.to_s)) : cv.cell
+ cv.cell = @output_cell
@output_layout_file = nil
else
@output_layout = RBA::Layout::new
@@ -1545,7 +1552,7 @@ CODE
@output_layers.each do |li|
if !present_layers[li]
- info = @def_layout.get_info(li)
+ info = output.get_info(li)
lp = RBA::LayerProperties::new
lp.source_layer = info.layer
lp.source_datatype = info.datatype
diff --git a/src/lay/lay/doc/about/drc_ref_global.xml b/src/lay/lay/doc/about/drc_ref_global.xml
index 623d30d1e..11f97782c 100644
--- a/src/lay/lay/doc/about/drc_ref_global.xml
+++ b/src/lay/lay/doc/about/drc_ref_global.xml
@@ -767,7 +767,8 @@ a new target will be set up.
"what" specifies what input to use. "what" be either
-- A string "@n" specifying output to a layout in the current panel
+- A string "@n" (n is an integer) specifying output to a layout in the current panel
+- A string "@+" specifying output to a new layout in the current panel
- A layout filename
- A Layout object
- A Cell object
diff --git a/src/laybasic/laybasic/layCellView.cc b/src/laybasic/laybasic/layCellView.cc
index 6c44e29de..4dfb9cf48 100644
--- a/src/laybasic/laybasic/layCellView.cc
+++ b/src/laybasic/laybasic/layCellView.cc
@@ -226,6 +226,9 @@ LayoutHandle::set_tech_name (const std::string &tn)
} else {
m_tech_name = std::string ();
}
+ if (mp_layout) {
+ mp_layout->add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), tn));
+ }
technology_changed_event ();
}
}
@@ -339,6 +342,14 @@ LayoutHandle::load (const db::LoadLayoutOptions &options, const std::string &tec
db::Reader reader (stream);
db::LayerMap new_lmap = reader.read (layout (), m_load_options);
+ // If there is no technology given and the reader reports one, use this one
+ if (technology.empty ()) {
+ std::string tech_from_reader = layout ().meta_info_value ("technology");
+ if (! tech_from_reader.empty ()) {
+ set_tech_name (tech_from_reader);
+ }
+ }
+
// Update the file's data:
file_watcher ().remove_file (filename ());
file_watcher ().add_file (filename ());
@@ -358,6 +369,12 @@ LayoutHandle::load ()
db::Reader reader (stream);
db::LayerMap new_lmap = reader.read (layout (), m_load_options);
+ // Attach the technology from the reader if it reports one
+ std::string tech_from_reader = layout ().meta_info_value ("technology");
+ if (! tech_from_reader.empty ()) {
+ set_tech_name (tech_from_reader);
+ }
+
// Update the file's data:
file_watcher ().remove_file (filename ());
file_watcher ().add_file (filename ());
diff --git a/src/laybasic/laybasic/layLibrariesView.cc b/src/laybasic/laybasic/layLibrariesView.cc
index 2e9d44483..5937baf6c 100644
--- a/src/laybasic/laybasic/layLibrariesView.cc
+++ b/src/laybasic/laybasic/layLibrariesView.cc
@@ -475,7 +475,7 @@ LibrariesView::search_editing_finished ()
}
void
-LibrariesView::middle_clicked (const QModelIndex &index)
+LibrariesView::middle_clicked (const QModelIndex & /*index*/)
{
// ... nothing yet ..
}
@@ -493,7 +493,7 @@ LibrariesView::clicked (const QModelIndex & /*index*/)
}
void
-LibrariesView::double_clicked (const QModelIndex &index)
+LibrariesView::double_clicked (const QModelIndex & /*index*/)
{
// ... nothing yet ..
}
diff --git a/src/laybasic/laybasic/layWidgets.cc b/src/laybasic/laybasic/layWidgets.cc
index 781a77671..84249cae2 100644
--- a/src/laybasic/laybasic/layWidgets.cc
+++ b/src/laybasic/laybasic/layWidgets.cc
@@ -1143,5 +1143,150 @@ void DecoratedLineEdit::resizeEvent (QResizeEvent * /*event*/)
}
}
+// -------------------------------------------------------------
+// InteractiveListWidget implementation
+
+InteractiveListWidget::InteractiveListWidget (QWidget *parent)
+ : QListWidget (parent)
+{
+ setSelectionMode (QAbstractItemView::ExtendedSelection);
+ setDragDropMode (QAbstractItemView::InternalMove);
}
+void
+InteractiveListWidget::set_values (const std::vector &values)
+{
+ clear ();
+ add_values (values);
+}
+
+std::vector
+InteractiveListWidget::get_values ()
+{
+ std::vector v;
+ v.reserve ((size_t) count ());
+ for (int i = 0; i < count (); ++i) {
+ v.push_back (tl::to_string (item (i)->text ()));
+ }
+ return v;
+}
+
+void
+InteractiveListWidget::add_value (const std::string &value)
+{
+ addItem (tl::to_qstring (value));
+ refresh_flags ();
+ clearSelection ();
+ setCurrentItem (item (count () - 1));
+}
+
+void
+InteractiveListWidget::add_values (const std::vector &values)
+{
+ for (std::vector::const_iterator i = values.begin (); i != values.end (); ++i) {
+ addItem (tl::to_qstring (*i));
+ }
+ refresh_flags ();
+ clearSelection ();
+}
+
+void
+InteractiveListWidget::delete_selected_items ()
+{
+ QStringList items;
+ for (int i = 0; i < count (); ++i) {
+ if (! item (i)->isSelected ()) {
+ items.push_back (item (i)->text ());
+ }
+ }
+
+ clear ();
+ for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) {
+ addItem (*f);
+ }
+ refresh_flags ();
+}
+
+void
+InteractiveListWidget::move_selected_items_up ()
+{
+ std::set selected;
+ for (int i = 0; i < count (); ++i) {
+ if (item (i)->isSelected ()) {
+ selected.insert (item (i)->text ());
+ }
+ }
+
+ QStringList items;
+ int j = -1;
+ for (int i = 0; i < count (); ++i) {
+ if (item (i)->isSelected ()) {
+ items.push_back (item (i)->text ());
+ } else {
+ if (j >= 0) {
+ items.push_back (item (j)->text ());
+ }
+ j = i;
+ }
+ }
+ if (j >= 0) {
+ items.push_back (item (j)->text ());
+ }
+
+ clear ();
+ for (QStringList::const_iterator f = items.begin (); f != items.end (); ++f) {
+ addItem (*f);
+ if (selected.find (*f) != selected.end ()) {
+ item (count () - 1)->setSelected (true);
+ }
+ }
+ refresh_flags ();
+}
+
+void
+InteractiveListWidget::move_selected_items_down ()
+{
+ std::set selected;
+ for (int i = 0; i < count (); ++i) {
+ if (item (i)->isSelected ()) {
+ selected.insert (item (i)->text ());
+ }
+ }
+
+ QStringList items;
+ int j = -1;
+ for (int i = count (); i > 0; ) {
+ --i;
+ if (item (i)->isSelected ()) {
+ items.push_back (item (i)->text ());
+ } else {
+ if (j >= 0) {
+ items.push_back (item (j)->text ());
+ }
+ j = i;
+ }
+ }
+ if (j >= 0) {
+ items.push_back (item (j)->text ());
+ }
+
+ clear ();
+ for (QStringList::const_iterator f = items.end (); f != items.begin (); ) {
+ --f;
+ addItem (*f);
+ if (selected.find (*f) != selected.end ()) {
+ item (count () - 1)->setSelected (true);
+ }
+ }
+ refresh_flags ();
+}
+
+void
+InteractiveListWidget::refresh_flags ()
+{
+ for (int i = 0; i < count (); ++i) {
+ item (i)->setFlags (Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
+ }
+}
+
+}
diff --git a/src/laybasic/laybasic/layWidgets.h b/src/laybasic/laybasic/layWidgets.h
index a18cb3337..df1333733 100644
--- a/src/laybasic/laybasic/layWidgets.h
+++ b/src/laybasic/laybasic/layWidgets.h
@@ -31,6 +31,7 @@
#include
#include
#include
+#include
#include
namespace db
@@ -460,6 +461,62 @@ private:
int m_default_left_margin, m_default_right_margin;
};
+/**
+ * @brief An interactive liste widget which offers slots to delete and move items and interfaces to std::vector
+ */
+class LAYBASIC_PUBLIC InteractiveListWidget
+ : public QListWidget
+{
+Q_OBJECT
+
+public:
+ /**
+ * @brief Constructor
+ */
+ InteractiveListWidget (QWidget *parent = 0);
+
+ /**
+ * @brief Sets the items in the widget
+ */
+ void set_values (const std::vector &values);
+
+ /**
+ * @brief Gets the items in the widget
+ */
+ std::vector get_values ();
+
+ /**
+ * @brief Adds a value
+ */
+ void add_value (const std::string &value);
+
+ /**
+ * @brief Adds values
+ */
+ void add_values (const std::vector &values);
+
+private slots:
+ /**
+ * @brief Deletes the selected items
+ */
+ void delete_selected_items ();
+
+ /**
+ * @brief Moves the selected items up
+ */
+ void move_selected_items_up ();
+
+ /**
+ * @brief Moves the selected items down
+ */
+ void move_selected_items_down ();
+
+private:
+ void refresh_flags ();
+
+ bool m_drag_and_drop_enabled;
+};
+
} // namespace lay
#endif
diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc
index c24a97933..af7e88467 100644
--- a/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc
+++ b/src/plugins/streamers/cif/db_plugin/dbCIFReader.cc
@@ -536,8 +536,6 @@ CIFReader::read_cell (db::Layout &layout, db::Cell &cell, double sf, int level)
} else if (c == 'L') {
- skip_blanks ();
-
++layer_specs;
std::string name = read_name ();
diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc
index 6422af9d8..141f00b54 100644
--- a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc
+++ b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.cc
@@ -61,11 +61,7 @@ CIFWriter::operator<<(const std::string &s)
CIFWriter &
CIFWriter::operator<<(endl_tag)
{
-#ifdef _WIN32
- *this << "\r\n";
-#else
*this << "\n";
-#endif
return *this;
}
@@ -78,6 +74,8 @@ CIFWriter::xy_sep () const
void
CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLayoutOptions &options)
{
+ stream.set_as_text (true);
+
m_options = options.get_options ();
mp_stream = &stream;
@@ -107,7 +105,7 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
strftime(timestr, sizeof (timestr), "%F %T", &tt);
// Write header
- *this << "(CIF file written " << (const char *)timestr << " by KLayout);" << endl;
+ *this << "(CIF file written " << (const char *)timestr << " by KLayout);" << m_endl;
// TODO: this can be done more intelligently ..
int tl_scale_divider;
@@ -136,8 +134,8 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
double sf = 1.0;
- *this << "DS " << cell_index << " " << tl_scale_denom << " " << tl_scale_divider << ";" << endl;
- *this << "9 " << tl::to_word_or_quoted_string (layout.cell_name (*cell)) << ";" << endl;
+ *this << "DS " << cell_index << " " << tl_scale_denom << " " << tl_scale_divider << ";" << m_endl;
+ *this << "9 " << tl::to_word_or_quoted_string (layout.cell_name (*cell)) << ";" << m_endl;
// instances
for (db::Cell::const_iterator inst = cref.begin (); ! inst.at_end (); ++inst) {
@@ -195,7 +193,7 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
*this << " T" << d.x() << xy_sep () << d.y();
- *this << ";" << endl;
+ *this << ";" << m_endl;
}
@@ -219,7 +217,7 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
}
// end of cell
- *this << "DF;" << endl;
+ *this << "DF;" << m_endl;
}
@@ -232,7 +230,7 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
std::map::const_iterator cif_index = db_to_cif_index_map.find (*cell);
tl_assert(cif_index != db_to_cif_index_map.end ());
- *this << "C" << cif_index->second << ";" << endl;
+ *this << "C" << cif_index->second << ";" << m_endl;
}
@@ -241,7 +239,7 @@ CIFWriter::write (db::Layout &layout, tl::OutputStream &stream, const db::SaveLa
}
// end of file
- *this << "E" << endl;
+ *this << "E" << m_endl;
m_progress.set (mp_stream->pos ());
@@ -252,7 +250,7 @@ CIFWriter::emit_layer()
{
if (m_needs_emit) {
m_needs_emit = false;
- *this << "L " << tl::to_word_or_quoted_string(m_layer.name, "0123456789_.$") << ";" << endl;
+ *this << "L " << tl::to_word_or_quoted_string (tl::to_upper_case (m_layer.name), "0123456789_.$") << ";" << m_endl;
}
}
@@ -266,12 +264,12 @@ CIFWriter::write_texts (const db::Layout &layout, const db::Cell &cell, unsigned
emit_layer ();
- *this << "94 " << tl::to_word_or_quoted_string(shape->text_string(), "0123456789:<>/&%$!.-_#+*?\\[]{}");
+ *this << "94 " << tl::to_word_or_quoted_string (shape->text_string(), "0123456789:<>/&%$!.-_#+*?\\[]{}");
double h = shape->text_size () * layout.dbu ();
db::Vector p (shape->text_trans ().disp () * sf);
- *this << " " << p.x() << xy_sep () << p.y () << " " << h << ";" << endl;
+ *this << " " << p.x() << xy_sep () << p.y () << " " << h << ";" << m_endl;
++shape;
@@ -324,7 +322,7 @@ CIFWriter::write_polygon (const db::Polygon &polygon, double sf)
db::Point pp (*p * sf);
*this << " " << pp.x () << xy_sep () << pp.y ();
}
- *this << ";" << endl;
+ *this << ";" << m_endl;
}
void
@@ -338,7 +336,7 @@ CIFWriter::write_boxes (const db::Layout & /*layout*/, const db::Cell &cell, uns
emit_layer ();
db::Box b (shape->bbox () * sf);
- *this << "B " << b.width () << " " << b.height () << " " << b.center ().x () << xy_sep () << b.center ().y () << ";" << endl;
+ *this << "B " << b.width () << " " << b.height () << " " << b.center ().x () << xy_sep () << b.center ().y () << ";" << m_endl;
++shape;
@@ -411,13 +409,13 @@ CIFWriter::write_paths (const db::Layout & /*layout*/, const db::Cell &cell, uns
db::Point pp (*shape->begin_point () * sf);
*this << " " << pp.x () << xy_sep () << pp.y ();
- *this << ";" << endl;
+ *this << ";" << m_endl;
} else if (path_type >= 0 && npts > 1) {
emit_layer ();
- *this << "98 " << path_type << ";" << endl;
+ *this << "98 " << path_type << ";" << m_endl;
*this << "W " << long (floor (0.5 + sf * shape->path_width ()));
@@ -426,7 +424,7 @@ CIFWriter::write_paths (const db::Layout & /*layout*/, const db::Cell &cell, uns
*this << " " << pp.x () << xy_sep () << pp.y ();
}
- *this << ";" << endl;
+ *this << ";" << m_endl;
} else {
db::Polygon poly;
diff --git a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h
index d5ade32f8..ab81244b7 100644
--- a/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h
+++ b/src/plugins/streamers/cif/db_plugin/dbCIFWriter.h
@@ -66,7 +66,7 @@ private:
tl::OutputStream *mp_stream;
CIFWriterOptions m_options;
tl::AbsoluteProgress m_progress;
- endl_tag endl;
+ endl_tag m_endl;
db::LayerProperties m_layer;
bool m_needs_emit;
diff --git a/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc b/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc
index ed41fb092..8290f56d8 100644
--- a/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc
+++ b/src/plugins/streamers/cif/db_plugin/gsiDeclDbCIF.cc
@@ -100,10 +100,9 @@ static
gsi::ClassExt cif_reader_options (
gsi::method_ext ("cif_set_layer_map", &set_layer_map, gsi::arg ("map"), gsi::arg ("create_other_layers"),
"@brief Sets the layer map\n"
- "This sets a layer mapping for the reader. The \"create_other_layers\" specifies whether to create layers that are not "
- "in the mapping and automatically assign layers to them.\n"
- "@param map The layer map to set."
- "@param create_other_layers The flag telling whether other layer should be created also. Set to false if just the layers in the mapping table should be read.\n"
+ "This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers, for example to assign layer/datatype numbers to the named layers.\n"
+ "@param map The layer map to set.\n"
+ "@param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map.\n"
"\n"
"This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions "
"in a format-specific fashion."
@@ -111,7 +110,7 @@ gsi::ClassExt cif_reader_options (
gsi::method_ext ("cif_layer_map=", &set_layer_map1, gsi::arg ("map"),
"@brief Sets the layer map\n"
"This sets a layer mapping for the reader. Unlike \\cif_set_layer_map, the 'create_other_layers' flag is not changed.\n"
- "@param map The layer map to set."
+ "@param map The layer map to set.\n"
"\n"
"This convenience method has been added in version 0.26."
) +
@@ -135,14 +134,17 @@ gsi::ClassExt cif_reader_options (
) +
gsi::method_ext ("cif_create_other_layers?", &create_other_layers,
"@brief Gets a value indicating whether other layers shall be created\n"
- "@return True, if other layers should be created.\n"
+ "@return True, if other layers will be created.\n"
+ "This attribute acts together with a layer map (see \\cif_layer_map=). Layers not listed in this map are created as well when "
+ "\\cif_create_other_layers? is true. Otherwise they are ignored.\n"
"\n"
"This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions "
"in a format-specific fashion."
) +
gsi::method_ext ("cif_create_other_layers=", &set_create_other_layers, gsi::arg ("create"),
"@brief Specifies whether other layers shall be created\n"
- "@param create True, if other layers should be created.\n"
+ "@param create True, if other layers will be created.\n"
+ "See \\cif_create_other_layers? for a description of this attribute.\n"
"\n"
"This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions "
"in a format-specific fashion."
@@ -212,7 +214,7 @@ static bool get_cif_blank_separator (const db::SaveLayoutOptions *options)
return options->get_options ().blank_separator;
}
-// extend lay::SaveLayoutOptions with the GDS2 options
+// extend lay::SaveLayoutOptions with the CIF options
static
gsi::ClassExt cif_writer_options (
gsi::method_ext ("cif_dummy_calls=", &set_cif_dummy_calls,
diff --git a/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc b/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc
index d2bce9774..af5266a8f 100755
--- a/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc
+++ b/src/plugins/streamers/dxf/db_plugin/gsiDeclDbDXF.cc
@@ -174,10 +174,9 @@ static
gsi::ClassExt dxf_reader_options (
gsi::method_ext ("dxf_set_layer_map", &set_layer_map, gsi::arg ("map"), gsi::arg ("create_other_layers"),
"@brief Sets the layer map\n"
- "This sets a layer mapping for the reader. The \"create_other_layers\" specifies whether to create layers that are not "
- "in the mapping and automatically assign layers to them.\n"
- "@param map The layer map to set."
- "@param create_other_layers The flag telling whether other layer should be created also. Set to false if just the layers in the mapping table should be read.\n"
+ "This sets a layer mapping for the reader. The layer map allows selection and translation of the original layers, for example to assign layer/datatype numbers to the named layers.\n"
+ "@param map The layer map to set.\n"
+ "@param create_other_layers The flag indicating whether other layers will be created as well. Set to false to read only the layers in the layer map.\n"
"\n"
"This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions "
"in a format-specific fashion."
@@ -185,7 +184,7 @@ gsi::ClassExt dxf_reader_options (
gsi::method_ext ("dxf_layer_map=", &set_layer_map1, gsi::arg ("map"),
"@brief Sets the layer map\n"
"This sets a layer mapping for the reader. Unlike \\dxf_set_layer_map, the 'create_other_layers' flag is not changed.\n"
- "@param map The layer map to set."
+ "@param map The layer map to set.\n"
"\n"
"This convenience method has been added in version 0.26."
) +
@@ -209,14 +208,17 @@ gsi::ClassExt dxf_reader_options (
) +
gsi::method_ext ("dxf_create_other_layers?", &create_other_layers,
"@brief Gets a value indicating whether other layers shall be created\n"
- "@return True, if other layers should be created.\n"
+ "@return True, if other layers will be created.\n"
+ "This attribute acts together with a layer map (see \\dxf_layer_map=). Layers not listed in this map are created as well when "
+ "\\dxf_create_other_layers? is true. Otherwise they are ignored.\n"
"\n"
"This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions "
"in a format-specific fashion."
) +
gsi::method_ext ("dxf_create_other_layers=", &set_create_other_layers, gsi::arg ("create"),
"@brief Specifies whether other layers shall be created\n"
- "@param create True, if other layers should be created.\n"
+ "@param create True, if other layers will be created.\n"
+ "See \\dxf_create_other_layers? for a description of this attribute.\n"
"\n"
"This method has been added in version 0.25 and replaces the respective global option in \\LoadLayoutOptions "
"in a format-specific fashion."
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..546d8d2e0
--- /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 "Magic files (*.MAG *.mag *.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 ("mag",
+ 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_member (&db::MAGReaderOptions::merge, "merge") +
+ 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 ("mag",
+ tl::make_member (&db::MAGWriterOptions::lambda, "lambda") +
+ tl::make_member (&db::MAGWriterOptions::tech, "tech") +
+ tl::make_member (&db::MAGWriterOptions::write_timestamp, "write-timestamp")
+ );
+ }
+};
+
+// 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..e0cc80f47
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGFormat.h
@@ -0,0 +1,199 @@
+
+/*
+
+ 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),
+ merge (true)
+ {
+ // .. 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 A flag indicating whether to merge boxes into polygons
+ *
+ * If this flag is set to true (the default), the boxes of the Magic
+ * layout files are merged into polygons.
+ */
+ bool merge;
+
+ /**
+ * @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 ()
+ : lambda (0.0), write_timestamp (true)
+ {
+ // .. nothing yet ..
+ }
+
+ /**
+ * @brief Specifies the lambda value for writing
+ *
+ * The lambda value is the basic scaling parameter.
+ * If this value is set to 0 or negative, the lambda value stored in the layout
+ * is used (meta data "lambda").
+ */
+ double lambda;
+
+ /**
+ * @brief Specifies the technology value for writing Magic files
+ *
+ * If this value is set an empty string, the technology store in the layout's
+ * "technology" meta data is used.
+ */
+ std::string tech;
+
+ /**
+ * @brief A value indicating whether the real (true) or fake (false) timestamp is written
+ *
+ * A fake, static timestamp is useful for comparing files.
+ */
+ bool write_timestamp;
+
+ /**
+ * @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..b1431d001
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.cc
@@ -0,0 +1,669 @@
+
+/*
+
+ 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 "dbShapeProcessor.h"
+#include "dbTechnology.h"
+
+#include "tlException.h"
+#include "tlString.h"
+#include "tlClassRegistry.h"
+#include "tlFileUtils.h"
+#include "tlUri.h"
+
+#include
+#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_merge (true), mp_klayout_tech (0)
+{
+ m_progress.set_format (tl::to_string (tr ("%.0fk lines")));
+ m_progress.set_format_unit (1000.0);
+ m_progress.set_unit (100000.0);
+
+ mp_current_stream = 0;
+}
+
+MAGReader::~MAGReader ()
+{
+ // .. nothing yet ..
+}
+
+const LayerMap &
+MAGReader::read (db::Layout &layout)
+{
+ return read (layout, db::LoadLayoutOptions ());
+}
+
+const LayerMap &
+MAGReader::read (db::Layout &layout, const db::LoadLayoutOptions &options)
+{
+ prepare_layers ();
+
+ mp_klayout_tech = 0;
+ std::string klayout_tech_name = layout.meta_info_value ("technology");
+ if (! klayout_tech_name.empty () && db::Technologies::instance ()->has_technology (klayout_tech_name)) {
+ mp_klayout_tech = db::Technologies::instance ()->technology_by_name (klayout_tech_name);
+ }
+
+ 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;
+ m_merge = specific_options.merge;
+ mp_current_stream = 0;
+
+ 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);
+
+ std::string top_cellname = cell_name_from_path (m_stream.source ());
+ db::cell_index_type top_cell;
+ if (layout.has_cell (top_cellname.c_str ())) {
+ top_cell = layout.cell_by_name (top_cellname.c_str ()).second;
+ } else {
+ top_cell = layout.add_cell (top_cellname.c_str ());
+ }
+
+ layout.dbu (m_dbu);
+
+ m_cells_to_read.clear ();
+ m_cells_read.clear ();
+ m_use_lib_paths.clear ();
+ m_dbu_trans_inv = db::CplxTrans (m_dbu).inverted ();
+ m_tech.clear ();
+
+ {
+ tl::SelfTimer timer (tl::verbosity () >= 21, "Reading MAGIC file tree");
+
+ // This is the seed
+ do_read (layout, top_cell, m_stream);
+
+ while (! m_cells_to_read.empty ()) {
+
+ std::pair > next = *m_cells_to_read.begin ();
+ m_cells_to_read.erase (m_cells_to_read.begin ());
+
+ tl::InputStream stream (next.second.first);
+ tl::TextInputStream text_stream (stream);
+ do_read (layout, next.second.second, text_stream);
+
+ }
+ }
+
+ finish_layers (layout);
+ return layer_map ();
+}
+
+void
+MAGReader::error (const std::string &msg)
+{
+ throw MAGReaderException (msg, mp_current_stream->line_number (), mp_current_stream->source ());
+}
+
+void
+MAGReader::warn (const std::string &msg)
+{
+ // TODO: compress
+ tl::warn << msg
+ << tl::to_string (tr (" (line=")) << mp_current_stream->line_number ()
+ << tl::to_string (tr (", file=")) << mp_current_stream->source ()
+ << ")";
+}
+
+db::cell_index_type
+MAGReader::cell_from_path (const std::string &path, db::Layout &layout)
+{
+ std::string cellname = tl::filename (path);
+
+ std::map::const_iterator c = m_cells_read.find (cellname);
+ if (c != m_cells_read.end ()) {
+ return c->second;
+ }
+
+ // NOTE: this can lead to cell variants if a cell is present with different library paths ... (L500_CHAR_p)
+ db::cell_index_type ci;
+ if (layout.has_cell (cellname.c_str ())) {
+ // NOTE: this reuses an existing cell and will add(!) the layout to the latter. This
+ // enables "incremental read" like for GDS files.
+ ci = layout.cell_by_name (cellname.c_str ()).second;
+ } else {
+ ci = layout.add_cell (cell_name_from_path (path).c_str ());
+ }
+ m_cells_read.insert (std::make_pair (cellname, ci));
+
+ std::string cell_file;
+ if (! resolve_path (path, layout, cell_file)) {
+ // skip with a warning if the file can't be opened (TODO: better to raise an error?)
+ tl::warn << tl::to_string (tr ("Unable to find a layout file for cell - skipping this cell: ")) << path;
+ layout.cell (ci).set_ghost_cell (true);
+ } else {
+ m_cells_to_read.insert (std::make_pair (cellname, std::make_pair (cell_file, ci)));
+ }
+
+ return ci;
+}
+
+std::string
+MAGReader::cell_name_from_path (const std::string &path)
+{
+ std::string file = tl::split (path, "/").back ();
+ return tl::split (file, ".").front ();
+}
+
+static bool find_and_normalize_file (const tl::URI &uri, std::string &path)
+{
+ // TODO: sync with plugin definition
+ static const char *extensions[] = {
+ ".mag", ".mag.gz", ".MAG", ".MAG.gz"
+ };
+
+ for (size_t e = 0; e < sizeof (extensions) / sizeof (extensions [0]); ++e) {
+
+ if (uri.scheme ().empty () || uri.scheme () == "file") {
+
+ std::string fp = uri.path () + extensions[e];
+
+ if (tl::verbosity () >= 30) {
+ tl::log << tl::to_string (tr ("Trying layout file: ")) << fp;
+ }
+
+ if (tl::file_exists (fp)) {
+ path = fp;
+ return true;
+ }
+
+ } else {
+
+ // TODO: this is not quite efficient, but the only thing we can do for now
+ tl::URI uri_with_ext = uri;
+ uri_with_ext.set_path (uri_with_ext.path () + extensions[e]);
+ std::string us = uri_with_ext.to_string ();
+
+ if (tl::verbosity () >= 30) {
+ tl::log << tl::to_string (tr ("Trying layout URI: ")) << us;
+ }
+
+ try {
+ tl::InputStream is (us);
+ if (is.get (1)) {
+ path = us;
+ return true;
+ }
+ } catch (...) {
+ // .. nothing yet ..
+ }
+
+ }
+
+ }
+
+ return false;
+}
+
+bool
+MAGReader::resolve_path (const std::string &path, const db::Layout & /*layout*/, std::string &real_path)
+{
+ tl::Eval expr;
+
+ // the variables supported for evaluation are
+ // "tech_name": the name of the KLayout technology this file is loaded for (this may be the Magic technology name)
+ // "tech_dir": the path to KLayout's technology folder for "tech_name" or the default technology's folder path
+ // "magic_tech": the technology name from the Magic file currently read
+
+ if (mp_klayout_tech) {
+ expr.set_var ("tech_dir", mp_klayout_tech->base_path ());
+ expr.set_var ("tech_name", mp_klayout_tech->name ());
+ } else {
+ expr.set_var ("tech_dir", std::string ("."));
+ expr.set_var ("tech_name", std::string ());
+ }
+ expr.set_var ("magic_tech", m_tech);
+
+ tl::URI path_uri (path);
+
+ // absolute URIs are kept - we just try to figure out the suffix
+ if (tl::is_absolute (path_uri.path ())) {
+ return find_and_normalize_file (path_uri, real_path);
+ }
+
+ tl::URI source_uri (mp_current_stream->source ());
+ source_uri.set_path (tl::dirname (source_uri.path ()));
+
+ // first attempt: try relative to source
+ if (find_and_normalize_file (source_uri.resolved (tl::URI (path)), real_path)) {
+ return true;
+ }
+
+ // then try relative to library paths
+ for (std::vector::const_iterator lp = m_lib_paths.begin (); lp != m_lib_paths.end (); ++lp) {
+ std::string lib_path = expr.interpolate (*lp);
+ if (find_and_normalize_file (source_uri.resolved (tl::URI (lib_path).resolved (tl::URI (path))), real_path)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+MAGReader::do_read (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream)
+{
+ try {
+
+ mp_current_stream = &stream;
+ do_read_part (layout, cell_index, stream);
+
+ if (m_merge) {
+ do_merge_part (layout, cell_index);
+ }
+
+ } catch (tl::Exception &ex) {
+ error (ex.msg ());
+ }
+}
+
+void
+MAGReader::do_read_part (db::Layout &layout, db::cell_index_type cell_index, tl::TextInputStream &stream)
+{
+ tl::SelfTimer timer (tl::verbosity () >= 31, "File read");
+
+ if (tl::verbosity () >= 30) {
+ tl::log << "Reading layout file: " << stream.source ();
+ }
+
+ std::string l = stream.get_line ();
+ if (l != "magic") {
+ error (tl::to_string (tr ("Could not find 'magic' header line - is this a MAGIC file?")));
+ }
+
+ layout.add_meta_info (db::MetaInfo ("lambda", "lambda value (tech scaling)", tl::to_string (m_lambda)));
+
+ bool valid_layer = false;
+ unsigned int current_layer = 0;
+ bool in_labels = false;
+
+ while (! stream.at_end ()) {
+
+ std::string l = stream.get_line ();
+ tl::Extractor ex (l.c_str ());
+
+ if (ex.at_end () || ex.test ("#")) {
+
+ // skip empty lines and comments
+ continue;
+
+ } else if (ex.test ("tech")) {
+
+ ex.read_word_or_quoted (m_tech);
+
+ if (&m_stream == &stream) {
+
+ // initial file - store technology
+ layout.add_meta_info (db::MetaInfo ("magic_technology", tl::to_string (tr ("MAGIC technology string")), m_tech));
+
+ // propose this is the KLayout technology unless a good one is given
+ if (! mp_klayout_tech) {
+ layout.add_meta_info (db::MetaInfo ("technology", tl::to_string (tr ("Technology name")), m_tech));
+ }
+
+ }
+
+ ex.expect_end ();
+
+ } else if (ex.test ("timestamp")) {
+
+ size_t ts = 0;
+ ex.read (ts);
+
+ if (&m_stream == &stream) {
+ // initial file - store timestamp
+ layout.add_meta_info (db::MetaInfo ("magic_timestamp", "MAGIC main file timestamp", tl::to_string (ts)));
+ }
+
+ ex.expect_end ();
+
+ } else if (ex.test ("<<")) {
+
+ std::string lname;
+ ex.read_word_or_quoted (lname);
+
+ if (lname == "end") {
+ in_labels = false;
+ valid_layer = false;
+ } else if (lname == "labels") {
+ in_labels = true;
+ } else if (lname == "checkpaint") {
+ // ignore "checkpaint" internal layer
+ in_labels = false;
+ valid_layer = false;
+ } else {
+ in_labels = false;
+ std::pair ll = open_layer (layout, lname);
+ valid_layer = ll.first;
+ current_layer = ll.second;
+ }
+
+ ex.expect (">>");
+ ex.expect_end ();
+
+ } else if (ex.test ("rect")) {
+
+ if (in_labels) {
+ error (tl::to_string (tr ("'rect' statement inside labels section")));
+ } else if (valid_layer) {
+ read_rect (ex, layout, cell_index, current_layer);
+ }
+
+ } else if (ex.test ("tri")) {
+
+ if (in_labels) {
+ error (tl::to_string (tr ("'rect' statement inside labels section")));
+ } else if (valid_layer) {
+ read_tri (ex, layout, cell_index, current_layer);
+ }
+
+ } else if (ex.test ("rlabel")) {
+
+ if (! in_labels) {
+ error (tl::to_string (tr ("'rlabel' statement outside labels section")));
+ } else {
+ read_rlabel (ex, layout, cell_index);
+ }
+
+ } else if (ex.test ("use")) {
+
+ read_cell_instance (ex, stream, layout, cell_index);
+
+ }
+
+ }
+}
+
+void
+MAGReader::do_merge_part (Layout &layout, cell_index_type cell_index)
+{
+ tl::SelfTimer timer (tl::verbosity () >= 31, "Merge step");
+
+ db::Cell &cell = layout.cell (cell_index);
+ db::ShapeProcessor sp;
+ if (tl::verbosity () >= 40) {
+ sp.enable_progress (tl::to_string (tr ("Merging shapes for MAG reader")));
+ } else {
+ sp.disable_progress ();
+ }
+ sp.set_base_verbosity (40);
+
+ std::vector saved_texts;
+
+ for (db::Layout::layer_iterator l = layout.begin_layers (); l != layout.end_layers (); ++l) {
+
+ unsigned int li = (unsigned int) (*l).first;
+ db::Shapes &shapes = cell.shapes (li);
+
+ // save texts before merge
+ saved_texts.clear ();
+ for (db::Shapes::shape_iterator t = shapes.begin (db::ShapeIterator::Texts); ! t.at_end (); ++t) {
+ saved_texts.push_back (db::Text ());
+ t->text (saved_texts.back ());
+ }
+
+ sp.merge (layout, cell, li, shapes, false);
+
+ // re-insert the texts
+ for (std::vector::const_iterator t = saved_texts.begin (); t != saved_texts.end (); ++t) {
+ shapes.insert (*t);
+ }
+
+ }
+}
+
+void
+MAGReader::read_rect (tl::Extractor &ex, Layout &layout, cell_index_type cell_index, unsigned int layer)
+{
+ double l, b, r, t;
+ ex.read (l);
+ ex.read (b);
+ ex.read (r);
+ ex.read (t);
+ ex.expect_end ();
+
+ db::DBox box (l, b, r, t);
+ layout.cell (cell_index).shapes (layer).insert ((box * m_lambda).transformed (m_dbu_trans_inv));
+}
+
+void
+MAGReader::read_tri (tl::Extractor &ex, Layout &layout, cell_index_type cell_index, unsigned int layer)
+{
+ double l, b, r, t;
+ ex.read (l);
+ ex.read (b);
+ ex.read (r);
+ ex.read (t);
+
+ bool s = false, e = false;
+ while (! ex.at_end ()) {
+ if (ex.test ("s")) {
+ s = true;
+ } else if (ex.test ("e")) {
+ e = true;
+ } else {
+ break;
+ }
+ }
+ ex.expect_end ();
+
+ std::vector pts;
+
+ if (s && e) {
+ pts.push_back (db::Point (l, b));
+ pts.push_back (db::Point (r, t));
+ pts.push_back (db::Point (r, b));
+ } else if (s) {
+ pts.push_back (db::Point (l, b));
+ pts.push_back (db::Point (l, t));
+ pts.push_back (db::Point (r, b));
+ } else if (e) {
+ pts.push_back (db::Point (r, b));
+ pts.push_back (db::Point (l, t));
+ pts.push_back (db::Point (r, t));
+ } else {
+ pts.push_back (db::Point (l, b));
+ pts.push_back (db::Point (l, t));
+ pts.push_back (db::Point (r, t));
+ }
+
+ db::SimplePolygon poly;
+ poly.assign_hull (pts.begin (), pts.end ());
+ layout.cell (cell_index).shapes (layer).insert ((poly * m_lambda).transformed (m_dbu_trans_inv));
+}
+
+void
+MAGReader::read_rlabel (tl::Extractor &ex, Layout &layout, cell_index_type cell_index)
+{
+ std::string lname;
+ ex.read (lname);
+
+ double l, b, r, t;
+ ex.read (l);
+ ex.read (b);
+ ex.read (r);
+ ex.read (t);
+
+ int pos = 0;
+ ex.read (pos);
+
+ ex.skip ();
+ db::DText text (ex.get (), db::DTrans ());
+
+ double x = 0.5 * (l + r);
+ double y = 0.5 * (b + t);
+ if (pos == 2 || pos == 3 || pos == 4) {
+ text.halign (db::HAlignRight);
+ x = r;
+ } else if (pos == 6 || pos == 7 || pos == 8) {
+ text.halign (db::HAlignLeft);
+ x = l;
+ } else {
+ text.halign (db::HAlignCenter);
+ }
+ if (pos == 1 || pos == 2 || pos == 8) {
+ text.valign (db::VAlignTop);
+ y = t;
+ } else if (pos == 4 || pos == 5 || pos == 6) {
+ text.valign (db::VAlignBottom);
+ y = b;
+ } else {
+ text.valign (db::VAlignCenter);
+ }
+
+ text.move (db::DVector (x, y));
+
+ if (true || lname != "space") { // really? "space"? ignore it?
+ std::pair ll = open_layer (layout, lname);
+ if (ll.first) {
+ layout.cell (cell_index).shapes (ll.second).insert ((text * m_lambda).transformed (m_dbu_trans_inv));
+ }
+ }
+}
+
+void
+MAGReader::read_cell_instance (tl::Extractor &ex, tl::TextInputStream &stream, Layout &layout, cell_index_type cell_index)
+{
+ const char *include_chars_in_files = "$_,.-$+#:;[]()<>|/\\";
+
+ std::string filename, use_id, lib_path;
+ ex.read_word_or_quoted (filename, include_chars_in_files);
+ if (! ex.at_end ()) {
+ ex.read_word_or_quoted (use_id);
+ }
+ if (! ex.at_end ()) {
+ ex.read_word_or_quoted (lib_path, include_chars_in_files);
+ }
+
+ if (lib_path.empty ()) {
+ std::map::const_iterator lp = m_use_lib_paths.find (filename);
+ if (lp != m_use_lib_paths.end ()) {
+ lib_path = lp->second;
+ }
+ } else {
+ // give precendence to lib_path
+ filename = tl::filename (filename);
+ // save for next use
+ m_use_lib_paths.insert (std::make_pair (filename, lib_path));
+ }
+
+ if (! lib_path.empty ()) {
+ // NOTE: we don't use the system separator because it looks like MAG files use "/".
+ filename = lib_path + "/" + filename;
+ }
+
+ // read more lines until box
+
+ db::DVector a, b, p;
+ unsigned long na = 1, nb = 1;
+
+ db::DCplxTrans trans;
+
+ while (! stream.at_end ()) {
+
+ std::string l = stream.get_line ();
+ tl::Extractor ex2 (l.c_str ());
+
+ if (ex2.at_end () || ex2.test ("#")) {
+ continue;
+ } else if (ex2.test ("array")) {
+
+ int xlo = 0, xhi = 0, ylo = 0, yhi = 0;
+ double xsep = 0.0, ysep = 0.0;
+
+ ex2.read (xlo);
+ ex2.read (xhi);
+ ex2.read (xsep);
+
+ ex2.read (ylo);
+ ex2.read (yhi);
+ ex2.read (ysep);
+
+ na = (unsigned long) std::max (0, xhi - xlo + 1);
+ a = db::DVector (xsep, 0) * m_lambda;
+ nb = (unsigned long) std::max (0, yhi - ylo + 1);
+ b = db::DVector (0, ysep) * m_lambda;
+
+ } else if (ex2.test ("timestamp")) {
+ // ignored
+ } else if (ex2.test ("transform")) {
+
+ double m11 = 0.0, m12 = 0.0, m21 = 0.0, m22 = 0.0;
+ double dx = 0.0, dy = 0.0;
+
+ ex2.read (m11);
+ ex2.read (m12);
+ ex2.read (dx);
+ ex2.read (m21);
+ ex2.read (m22);
+ ex2.read (dy);
+
+ trans = db::DCplxTrans (db::Matrix2d (m11, m12, m21, m22), db::DVector (dx, dy) * m_lambda);
+
+ } else if (ex2.test ("box")) {
+ // ignored
+ break;
+ }
+
+ }
+
+ // create the instance
+
+ a = trans * a;
+ b = trans * b;
+
+ db::cell_index_type ci = cell_from_path (filename, layout);
+
+ db::ICplxTrans itrans = m_dbu_trans_inv * trans * db::CplxTrans (m_dbu);
+
+ if (na == 1 && nb == 1) {
+ layout.cell (cell_index).insert (db::CellInstArray (db::CellInst (ci), itrans));
+ } else {
+ layout.cell (cell_index).insert (db::CellInstArray (db::CellInst (ci), itrans, m_dbu_trans_inv * a, m_dbu_trans_inv * b, na, nb));
+ }
+}
+
+}
+
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..8154a34a0
--- /dev/null
+++ b/src/plugins/streamers/magic/db_plugin/dbMAGReader.h
@@ -0,0 +1,165 @@
+
+/*
+
+ 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