/* KLayout Layout Viewer Copyright (C) 2006-2021 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 "dbOASISReader.h" #include "dbCommonReader.h" #include "dbStream.h" #include "dbObjectWithProperties.h" #include "dbArray.h" #include "dbStatic.h" #include "tlException.h" #include "tlString.h" #include "tlClassRegistry.h" namespace db { // --------------------------------------------------------------- // --------------------------------------------------------------- // OASISReader OASISReader::OASISReader (tl::InputStream &s) : m_stream (s), m_progress (tl::to_string (tr ("Reading OASIS file")), 10000), m_dbu (0.001), m_expect_strict_mode (-1), mm_repetition (this, "repetition"), mm_placement_cell (this, "placement-cell"), mm_placement_x (this, "playcement-x"), mm_placement_y (this, "playcement-y"), mm_layer (this, "layer"), mm_datatype (this, "datatype"), mm_textlayer (this, "textlayer"), mm_texttype (this, "texttype"), mm_text_x (this, "text-x"), mm_text_y (this, "text-y"), mm_text_string (this, "text-string"), mm_text_string_id (this, "text-string-id"), mm_geometry_x (this, "geometry-x"), mm_geometry_y (this, "geometry-y"), mm_geometry_w (this, "geometry-w"), mm_geometry_h (this, "geometry-h"), mm_polygon_point_list (this, "polygon-point-list"), mm_path_halfwidth (this, "path-halfwidth"), mm_path_start_extension (this, "path-start-extension"), mm_path_end_extension (this, "path-end-extension"), mm_path_point_list (this, "path-point-list"), mm_ctrapezoid_type (this, "ctrapezoid-type"), mm_circle_radius (this, "circle-radius"), mm_last_property_name (this, "last-property-name"), mm_last_property_is_sprop (this, "last-property-is-stdprop"), mm_last_value_list(this, "last-value-list"), m_read_texts (true), m_read_properties (true), m_read_all_properties (false), m_s_gds_property_name_id (0), m_klayout_context_property_name_id (0) { m_progress.set_format (tl::to_string (tr ("%.0f MB"))); m_progress.set_unit (1024 * 1024); m_first_cellname = 0; m_first_propname = 0; m_first_propstring = 0; m_first_textstring = 0; m_first_layername = 0; m_in_table = NotInTable; m_table_cellname = 0; m_table_propname = 0; m_table_propstring = 0; m_table_textstring = 0; m_table_layername = 0; m_table_start = 0; } OASISReader::~OASISReader () { // .. nothing yet .. } void OASISReader::init (const db::LoadLayoutOptions &options) { CommonReader::init (options); m_read_texts = common_options ().enable_text_objects; m_read_properties = common_options ().enable_properties; db::OASISReaderOptions oasis_options = options.get_options (); m_read_all_properties = oasis_options.read_all_properties; m_expect_strict_mode = oasis_options.expect_strict_mode; } inline long long OASISReader::get_long_long () { unsigned long long u = get_ulong_long (); if ((u & 1) != 0) { return -(long long) (u >> 1); } else { return (long long) (u >> 1); } } inline unsigned long long OASISReader::get_ulong_long () { unsigned long long v = 0; unsigned long long vm = 1; char c; do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { error (tl::to_string (tr ("Unexpected end-of-file"))); return 0; } c = *b; if (vm > std::numeric_limits ::max () / 128 && (unsigned long long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } v += (unsigned long long) (c & 0x7f) * vm; vm <<= 7; } while ((c & 0x80) != 0); return v; } inline long OASISReader::get_long () { unsigned long u = get_ulong (); if ((u & 1) != 0) { return -long (u >> 1); } else { return long (u >> 1); } } inline unsigned long OASISReader::get_ulong_for_divider () { unsigned long l = get_ulong (); if (l == 0) { error (tl::to_string (tr ("Divider must not be zero"))); } return l; } inline unsigned long OASISReader::get_ulong () { unsigned long v = 0; unsigned long vm = 1; char c; do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { error (tl::to_string (tr ("Unexpected end-of-file"))); return 0; } c = *b; if (vm > std::numeric_limits ::max () / 128 && (unsigned long) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned long value overflow"))); } v += (unsigned long) (c & 0x7f) * vm; vm <<= 7; } while ((c & 0x80) != 0); return v; } inline int OASISReader::get_int () { unsigned int u = get_uint (); if ((u & 1) != 0) { return -int (u >> 1); } else { return int (u >> 1); } } inline unsigned int OASISReader::get_uint () { unsigned int v = 0; unsigned int vm = 1; char c; do { unsigned char *b = (unsigned char *) m_stream.get (1); if (! b) { error (tl::to_string (tr ("Unexpected end-of-file"))); return 0; } c = *b; if (vm > std::numeric_limits ::max () / 128 && (unsigned int) (c & 0x7f) > (std::numeric_limits ::max () / vm)) { error (tl::to_string (tr ("Unsigned integer value overflow"))); } v += (unsigned int) (c & 0x7f) * vm; vm <<= 7; } while ((c & 0x80) != 0); return v; } std::string OASISReader::get_str () { std::string s; get_str (s); return s; } void OASISReader::get_str (std::string &s) { size_t l = 0; get (l); char *b = (char *) m_stream.get (l); if (b) { s.assign (b, l); } else { s = std::string (); } } double OASISReader::get_real () { unsigned int t = get_uint (); if (t == 0) { return double (get_ulong ()); } else if (t == 1) { return -double (get_ulong ()); } else if (t == 2) { return 1.0 / double (get_ulong_for_divider ()); } else if (t == 3) { return -1.0 / double (get_ulong_for_divider ()); } else if (t == 4) { double d = double (get_ulong ()); return d / double (get_ulong_for_divider ()); } else if (t == 5) { double d = double (get_ulong ()); return -d / double (get_ulong_for_divider ()); } else if (t == 6) { union { float f; uint32_t i; } i2f; unsigned char *b = (unsigned char *) m_stream.get (sizeof (i2f.i)); if (! b) { error (tl::to_string (tr ("Unexpected end-of-file"))); } i2f.i = 0; b += sizeof (i2f.i); for (unsigned int i = 0; i < sizeof (i2f.i); ++i) { i2f.i = (i2f.i << 8) + uint32_t (*--b); } return double (i2f.f); } else if (t == 7) { union { double d; uint64_t i; } i2f; unsigned char *b = (unsigned char *) m_stream.get (sizeof (i2f.i)); if (! b) { error (tl::to_string (tr ("Unexpected end-of-file"))); } i2f.i = 0; b += sizeof (i2f.i); for (unsigned int i = 0; i < sizeof (i2f.i); ++i) { i2f.i = (i2f.i << 8) + uint64_t (*--b); } return double (i2f.d); } else { error (tl::sprintf (tl::to_string (tr ("Invalid real type %d")), t)); return 0.0; } } db::Coord OASISReader::get_ucoord (unsigned long grid) { unsigned long long lx = 0; get (lx); lx *= grid; if (lx > (unsigned long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } return db::Coord (lx); } OASISReader::distance_type OASISReader::get_ucoord_as_distance (unsigned long grid) { unsigned long long lx = 0; get (lx); lx *= grid; if (lx > (unsigned long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } return distance_type (lx); } db::Coord OASISReader::get_coord (long grid) { long long lx = 0; get (lx); lx *= grid; if (lx < (long long) (std::numeric_limits ::min ()) || lx > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } return db::Coord (lx); } db::Vector OASISReader::get_2delta (long grid) { unsigned long long l1 = 0; get (l1); long long lx = l1 >> 2; lx *= grid; if (lx > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } db::Coord x = lx; switch (l1 & 3) { case 0: return db::Vector (x, 0); case 1: return db::Vector (0, x); case 2: return db::Vector (-x, 0); case 3: default: return db::Vector (0, -x); } } db::Vector OASISReader::get_3delta (long grid) { unsigned long long l1 = 0; get (l1); long long lx = l1 >> 3; lx *= grid; if (lx > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } db::Coord x = lx; switch (l1 & 7) { case 0: return db::Vector (x, 0); case 1: return db::Vector (0, x); case 2: return db::Vector (-x, 0); case 3: return db::Vector (0, -x); case 4: return db::Vector (x, x); case 5: return db::Vector (-x, x); case 6: return db::Vector (-x, -x); case 7: default: return db::Vector (x, -x); } } db::Vector OASISReader::get_gdelta (long grid) { unsigned long long l1 = 0; get (l1); if ((l1 & 1) != 0) { long long lx = ((l1 & 2) == 0 ? (long long) (l1 >> 2) : -(long long) (l1 >> 2)); lx *= grid; if (lx < (long long) (std::numeric_limits ::min ()) || lx > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } long long ly; get (ly); ly *= grid; if (ly < (long long) (std::numeric_limits ::min ()) || ly > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } return db::Vector (db::Coord (lx), db::Coord (ly)); } else { long long lx = l1 >> 4; lx *= grid; if (lx > (long long) (std::numeric_limits ::max ())) { error (tl::to_string (tr ("Coordinate value overflow"))); } db::Coord x = lx; switch ((l1 >> 1) & 7) { case 0: return db::Vector (x, 0); case 1: return db::Vector (0, x); case 2: return db::Vector (-x, 0); case 3: return db::Vector (0, -x); case 4: return db::Vector (x, x); case 5: return db::Vector (-x, x); case 6: return db::Vector (-x, -x); case 7: default: return db::Vector (x, -x); } } } void OASISReader::error (const std::string &msg) { throw OASISReaderException (msg, m_stream.pos (), m_cellname.c_str ()); } void OASISReader::warn (const std::string &msg) { if (warnings_as_errors ()) { error (msg); } else { // TODO: compress tl::warn << msg << tl::to_string (tr (" (position=")) << m_stream.pos () << tl::to_string (tr (", cell=")) << m_cellname << ")"; } } /** * @brief A helper class to join two datatype layer name map members */ struct LNameJoinOp1 { void operator() (std::string &a, const std::string &b) { join_layer_names (a, b); } }; /** * @brief A helper class to join two layer map members * This implementation basically merged the datatype maps. */ struct LNameJoinOp2 { void operator() (tl::interval_map &a, const tl::interval_map &b) { LNameJoinOp1 op1; a.add (b.begin (), b.end (), op1); } }; /** * @brief Marks the beginning of a new table * * This method will update m_table_start which is the location used as * the start position of a strict mode table. Every record except CBLOCK * will update this position to point after the record. Hence m_table_start * points to the beginning of a table when PROPNAME, CELLNAME or any * other table-contained record is encountered. * Since CBLOCK does not update this record, the position of the table will * be the location of CBLOCK rather than that of the name record itself. * PAD records will also call this method, so the beginning of a table * is right after any preceding PAD records and exactly at the location * of the first name record after PADs. */ void OASISReader::mark_start_table () { // we need to this this to really finish a CBLOCK - this is a flaw // in the inflating reader, but it's hard to fix. get_byte (); m_stream.unget (1); // now we can fetch the position m_table_start = m_stream.pos (); } void OASISReader::read_offset_table () { unsigned long of = 0; of = get_uint (); m_table_cellname = get_ulong (); if (m_table_cellname != 0 && m_expect_strict_mode >= 0 && ((of == 0) != (m_expect_strict_mode == 0))) { warn (tl::to_string (tr ("CELLNAME offset table has unexpected strict mode"))); } of = get_uint (); m_table_textstring = get_ulong (); if (m_table_textstring != 0 && m_expect_strict_mode >= 0 && ((of == 0) != (m_expect_strict_mode == 0))) { warn (tl::to_string (tr ("TEXTSTRING offset table has unexpected strict mode"))); } of = get_uint (); m_table_propname = get_ulong (); if (m_table_propname != 0 && m_expect_strict_mode >= 0 && ((of == 0) != (m_expect_strict_mode == 0))) { warn (tl::to_string (tr ("PROPNAME offset table has unexpected strict mode"))); } of = get_uint (); m_table_propstring = get_ulong (); if (m_table_propstring != 0 && m_expect_strict_mode >= 0 && ((of == 0) != (m_expect_strict_mode == 0))) { warn (tl::to_string (tr ("PROPSTRING offset table has unexpected strict mode"))); } of = get_uint (); m_table_layername = get_ulong (); if (m_table_layername != 0 && m_expect_strict_mode >= 0 && ((of == 0) != (m_expect_strict_mode == 0))) { warn (tl::to_string (tr ("LAYERNAME offset table has unexpected strict mode"))); } // XNAME table ignored currently get_uint (); get_ulong (); } static const char magic_bytes[] = { "%SEMI-OASIS\015\012" }; void OASISReader::do_read (db::Layout &layout) { unsigned char r; char *mb; // prepare m_s_gds_property_name_id = layout.properties_repository ().prop_name_id ("S_GDS_PROPERTY"); m_klayout_context_property_name_id = layout.properties_repository ().prop_name_id ("KLAYOUT_CONTEXT"); // read magic bytes mb = (char *) m_stream.get (sizeof (magic_bytes) - 1); if (! mb) { error (tl::to_string (tr ("File too short"))); return; } if (strncmp (mb, magic_bytes, sizeof (magic_bytes) - 1) != 0) { error (tl::to_string (tr ("Format error (missing magic bytes)"))); } // read first record r = get_byte (); if (r != 1 /*START*/) { error (tl::to_string (tr ("Format error (START record expected)"))); } std::string v = get_str (); if (v != "1.0") { error (tl::sprintf (tl::to_string (tr ("Format error (only version 1.0 is supported, file has version %s)")), v)); } double res = get_real (); if (res < 1e-6) { error (tl::sprintf (tl::to_string (tr ("Invalid resolution of %g")), res)); } // compute database unit in pixel per meter m_dbu = 1.0e-6 / res; layout.dbu (m_dbu * 1e6); // read over table offsets if required bool table_offsets_at_end = get_uint (); if (! table_offsets_at_end) { read_offset_table (); } // reset the strict mode checking locations m_first_cellname = 0; m_first_propname = 0; m_first_propstring = 0; m_first_textstring = 0; m_first_layername = 0; m_in_table = NotInTable; m_table_cellname = 0; m_table_propname = 0; m_table_propstring = 0; m_table_textstring = 0; m_table_layername = 0; // define the name id counters unsigned long cellname_id = 0; unsigned long textstring_id = 0; unsigned long propstring_id = 0; unsigned long propname_id = 0; // id mode (explicit or implicit) enum id_mode { any, expl, impl }; id_mode cellname_id_mode = any; id_mode textstring_id_mode = any; id_mode propstring_id_mode = any; id_mode propname_id_mode = any; m_cellname_properties.clear (); m_textstrings.clear (); m_propstrings.clear (); m_propnames.clear (); m_instances.clear (); m_instances_with_props.clear (); db::PropertiesRepository::properties_set layout_properties; mark_start_table (); // read next record while (true) { r = get_byte (); if (r == 0 /*PAD*/) { // simply skip. mark_start_table (); } else if (r == 2 /*END*/) { // done break; } else if (r == 3 || r == 4 /*CELLNAME*/) { if (m_first_cellname == 0) { m_first_cellname = m_table_start; } else if (m_expect_strict_mode == 1 && m_in_table != InCELLNAME && m_first_cellname != 0) { warn (tl::to_string (tr ("CELLNAME outside table in strict mode"))); } m_in_table = InCELLNAME; // there cannot be more file level properties .. store what we have if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } // read a cell name std::string name = get_str (); // and the associated id unsigned long id = cellname_id; if (r == 3) { if (cellname_id_mode == expl) { error (tl::to_string (tr ("Explicit and implicit CELLNAME modes cannot be mixed"))); } cellname_id_mode = impl; ++cellname_id; } else { if (cellname_id_mode == impl) { error (tl::to_string (tr ("Explicit and implicit CELLNAME modes cannot be mixed"))); } cellname_id_mode = expl; get (id); } rename_cell (layout, id, name); reset_modal_variables (); std::pair pp = read_element_properties (layout.properties_repository (), true); if (pp.first) { m_cellname_properties.insert (std::make_pair (id, pp.second)); } } else if (r == 5 || r == 6 /*TEXTSTRING*/) { if (m_first_textstring == 0) { m_first_textstring = m_table_start; } else if (m_expect_strict_mode == 1 && m_in_table != InTEXTSTRING && m_first_textstring != 0) { warn (tl::to_string (tr ("TEXTSTRING outside table in strict mode"))); } m_in_table = InTEXTSTRING; // there cannot be more file level properties .. store what we have if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } // read a text string std::string name = get_str (); // and the associated id unsigned long id = textstring_id; if (r == 5) { if (textstring_id_mode == expl) { error (tl::to_string (tr ("Explicit and implicit TEXTSTRING modes cannot be mixed"))); } textstring_id_mode = impl; ++textstring_id; } else { if (textstring_id_mode == impl) { error (tl::to_string (tr ("Explicit and implicit TEXTSTRING modes cannot be mixed"))); } textstring_id_mode = expl; get (id); } if (! m_textstrings.insert (std::make_pair (id, name)).second) { error (tl::sprintf (tl::to_string (tr ("A TEXTSTRING with id %ld is present already")), id)); } reset_modal_variables (); // ignore properties attached to this name item read_element_properties (layout.properties_repository (), true); } else if (r == 7 || r == 8 /*PROPNAME*/) { if (m_first_propname == 0) { m_first_propname = m_table_start; } else if (m_expect_strict_mode == 1 && m_in_table != InPROPNAME && m_first_propname != 0) { warn (tl::to_string (tr ("PROPNAME outside table in strict mode"))); } m_in_table = InPROPNAME; // there cannot be more file level properties .. store what we have if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } // read a property name std::string name = get_str (); // and the associated id unsigned long id = propname_id; if (r == 7) { if (propname_id_mode == expl) { error (tl::to_string (tr ("Explicit and implicit PROPNAME modes cannot be mixed"))); } propname_id_mode = impl; ++propname_id; } else { if (propname_id_mode == impl) { error (tl::to_string (tr ("Explicit and implicit PROPNAME modes cannot be mixed"))); } propname_id_mode = expl; get (id); } if (! m_propnames.insert (std::make_pair (id, name)).second) { error (tl::sprintf (tl::to_string (tr ("A PROPNAME with id %ld is present already")), id)); } // resolve forward references to property names std::map ::iterator pf = m_propname_forward_references.find (id); if (pf != m_propname_forward_references.end ()) { if (name == "S_GDS_PROPERTY") { db::PropertiesRepository &rep = layout.properties_repository (); db::property_names_id_type s_gds_name_id = pf->second; // exchange the properties in the repository: first locate all // property sets that are affected std::vector pids; for (db::PropertiesRepository::iterator p = rep.begin (); p != rep.end (); ++p) { if (p->second.find (s_gds_name_id) != p->second.end ()) { pids.push_back (p->first); } } // create new property sets for the ones we found for (std::vector ::const_iterator pid = pids.begin (); pid != pids.end (); ++pid) { const db::PropertiesRepository::properties_set &old_set = rep.properties (*pid); db::PropertiesRepository::properties_set new_set; for (db::PropertiesRepository::properties_set::const_iterator s = old_set.begin (); s != old_set.end (); ++s) { if (s->first == s_gds_name_id) { if (!s->second.is_list () || s->second.get_list ().size () != 2) { error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); } new_set.insert (std::make_pair (rep.prop_name_id (s->second.get_list () [0]), s->second.get_list () [1])); } else { new_set.insert (*s); } } rep.change_properties (*pid, new_set); } } layout.properties_repository ().change_name (pf->second, tl::Variant (name)); m_propname_forward_references.erase (pf); } reset_modal_variables (); // ignore properties attached to this name item read_element_properties (layout.properties_repository (), true); } else if (r == 9 || r == 10 /*PROPSTRING*/) { if (m_first_propstring == 0) { m_first_propstring = m_table_start; } else if (m_expect_strict_mode == 1 && m_in_table != InPROPSTRING && m_first_propstring != 0) { warn (tl::to_string (tr ("PROPSTRING outside table in strict mode"))); } m_in_table = InPROPSTRING; // there cannot be more file level properties .. store what we have if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } // read a property string std::string name = get_str (); // and the associated id unsigned long id = propstring_id; if (r == 9) { if (propstring_id_mode == expl) { error (tl::to_string (tr ("Explicit and implicit PROPSTRING modes cannot be mixed"))); } propstring_id_mode = impl; ++propstring_id; } else { if (propstring_id_mode == impl) { error (tl::to_string (tr ("Explicit and implicit PROPSTRING modes cannot be mixed"))); } propstring_id_mode = expl; get (id); } if (! m_propstrings.insert (std::make_pair (id, name)).second) { error (tl::sprintf (tl::to_string (tr ("A PROPSTRING with id %ld is present already")), id)); } std::map::iterator fw = m_propvalue_forward_references.find (id); if (fw != m_propvalue_forward_references.end ()) { fw->second = name; } reset_modal_variables (); // ignore properties attached to this name item read_element_properties (layout.properties_repository (), true); } else if (r == 11 || r == 12 /*LAYERNAME*/) { if (m_first_layername == 0) { m_first_layername = m_table_start; } else if (m_expect_strict_mode == 1 && m_in_table != InLAYERNAME && m_first_layername != 0) { warn (tl::to_string (tr ("LAYERNAME outside table in strict mode"))); } m_in_table = InLAYERNAME; // there cannot be more file level properties .. store what we have if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } // read a layer name std::string name = get_str (); db::ld_type dt1 = 0, dt2 = std::numeric_limits::max () - 1; db::ld_type l1 = 0, l2 = std::numeric_limits::max () - 1; unsigned int it; it = get_uint (); if (it == 0) { // keep limits } else if (it == 1) { l2 = get_uint (); } else if (it == 2) { l1 = get_uint (); } else if (it == 3) { l1 = get_uint (); l2 = l1; } else if (it == 4) { l1 = get_uint (); l2 = get_uint (); } else { error (tl::to_string (tr ("Invalid LAYERNAME interval mode (layer)"))); } it = get_uint (); if (it == 0) { // keep limits } else if (it == 1) { dt2 = get_uint (); } else if (it == 2) { dt1 = get_uint (); } else if (it == 3) { dt1 = get_uint (); dt2 = dt1; } else if (it == 4) { dt1 = get_uint (); dt2 = get_uint (); } else { error (tl::to_string (tr ("Invalid LAYERNAME interval mode (datatype)"))); } // add to the layer name map tl::interval_map dt_map; LNameJoinOp1 op1; dt_map.add (dt1, dt2 + 1, name, op1); LNameJoinOp2 op2; layer_names ().add (l1, l2 + 1, dt_map, op2); reset_modal_variables (); // ignore properties attached to this name item read_element_properties (layout.properties_repository (), true); } else if (r == 28 || r == 29 /*PROPERTY*/) { // unrecognized property: store in layout properties if (r == 28) { read_properties (layout.properties_repository ()); } store_last_properties (layout.properties_repository (), layout_properties, true); mark_start_table (); } else if (r == 30 || r == 31 /*XNAME*/) { // there cannot be more file level properties .. store what we have if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } // read a XNAME: it is simply ignored get_ulong (); get_str (); if (r == 31) { get_ulong (); } reset_modal_variables (); // ignore properties attached to this name item read_element_properties (layout.properties_repository (), true); } else if (r == 13 || r == 14 /*CELL*/) { m_in_table = NotInTable; // there cannot be more file level properties .. store what we have if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } db::cell_index_type cell_index = 0; // read a cell if (r == 13) { unsigned long id = 0; get (id); std::pair cc = cell_by_id (id); if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) { error (tl::sprintf (tl::to_string (tr ("A cell with id %ld is defined already")), id)); } cell_index = make_cell (layout, id); m_cellname = name_for_id (id); if (m_cellname.empty ()) { m_cellname = std::string ("#") + tl::to_string (id); } } else { if (m_expect_strict_mode == 1) { warn (tl::to_string (tr ("CELL names must be references to CELLNAME ids in strict mode"))); } std::string name = get_str (); std::pair cc = cell_by_name (name); if (cc.first && ! layout.cell (cc.second).is_ghost_cell ()) { error (tl::sprintf (tl::to_string (tr ("A cell with name %s is defined already")), name.c_str ())); } cell_index = make_cell (layout, name); m_cellname = name; } reset_modal_variables (); mark_start_table (); do_read_cell (cell_index, layout); } else if (r == 34 /*CBLOCK*/) { unsigned int type = get_uint (); if (type != 0) { error (tl::sprintf (tl::to_string (tr ("Invalid CBLOCK compression type %d")), type)); } get_uint (); // uncomp-byte-count - not needed get_uint (); // comp-byte-count - not needed // put the stream into deflating mode m_stream.inflate (); } else { error (tl::sprintf (tl::to_string (tr ("Invalid record type on global level %d")), int (r))); } } if (! layout_properties.empty ()) { layout.prop_id (layout.properties_repository ().properties_id (layout_properties)); layout_properties.clear (); } size_t pt = m_stream.pos (); if (table_offsets_at_end) { read_offset_table (); } // read over tail and discard mb = (char *) m_stream.get (pt + 254 - m_stream.pos ()); if (! mb) { error (tl::to_string (tr ("Format error (too few bytes after END record)"))); } // check if there are no more bytes mb = (char *) m_stream.get (254); if (mb) { error (tl::to_string (tr ("Format error (too many bytes after END record)"))); } for (std::map ::const_iterator fw = m_text_forward_references.begin (); fw != m_text_forward_references.end (); ++fw) { std::map ::const_iterator ts = m_textstrings.find (fw->first); if (ts == m_textstrings.end ()) { error (tl::sprintf (tl::to_string (tr ("No text string defined for text string id %ld")), fw->first)); } else { layout.string_repository ().change_string_ref (fw->second, ts->second); } } // all forward references to property names must be resolved for (std::map ::const_iterator fw = m_propname_forward_references.begin (); fw != m_propname_forward_references.end (); ++fw) { error (tl::sprintf (tl::to_string (tr ("No property name defined for property name id %ld")), fw->first)); } // resolve all propvalue forward referenced if (! m_propvalue_forward_references.empty ()) { for (db::PropertiesRepository::non_const_iterator pi = layout.properties_repository ().begin_non_const (); pi != layout.properties_repository ().end_non_const (); ++pi) { for (db::PropertiesRepository::properties_set::iterator ps = pi->second.begin (); ps != pi->second.end (); ++ps) { if (ps->second.is_id ()) { unsigned long id = (unsigned long) ps->second.to_id (); std::map ::const_iterator fw = m_propvalue_forward_references.find (id); if (fw != m_propvalue_forward_references.end ()) { ps->second = tl::Variant (fw->second); } else { error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); } } else if (ps->second.is_list ()) { // Replace list elements as well // TODO: Q: can there be a list of lists? would need recursive replacement -> make that a method of tl::Variant const std::vector &l = ps->second.get_list (); bool needs_replacement = false; for (std::vector::const_iterator ll = l.begin (); ll != l.end () && ! needs_replacement; ++ll) { needs_replacement = ll->is_id (); } if (needs_replacement) { std::vector new_list (l); for (std::vector::iterator ll = new_list.begin (); ll != new_list.end (); ++ll) { if (ll->is_id ()) { unsigned long id = (unsigned long) ll->to_id (); std::map ::const_iterator fw = m_propvalue_forward_references.find (id); if (fw != m_propvalue_forward_references.end ()) { *ll = tl::Variant (fw->second); } else { error (tl::sprintf (tl::to_string (tr ("No property value defined for property value id %ld")), id)); } } } ps->second = tl::Variant (new_list.begin (), new_list.end ()); } } } } m_propvalue_forward_references.clear (); } // attach the properties found in CELLNAME to the cells (which may have other properties) for (std::map::const_iterator p = m_cellname_properties.begin (); p != m_cellname_properties.end (); ++p) { std::pair c = cell_by_id (p->first); if (c.first) { db::PropertiesRepository::properties_set cnp = layout.properties_repository ().properties (p->second); // Merge existing properties with the ones from CELLNAME db::Cell &cell = layout.cell (c.second); if (cell.prop_id () != 0) { db::PropertiesRepository::properties_set cp = layout.properties_repository ().properties (cell.prop_id ()); cnp.insert (cp.begin (), cp.end ()); } cell.prop_id (layout.properties_repository ().properties_id (cnp)); } } // Check the table offsets vs. real occurrence if (m_first_cellname != 0 && m_first_cellname != m_table_cellname && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("CELLNAME table offset does not match first occurrence of CELLNAME in strict mode - %s vs. %s")), m_table_cellname, m_first_cellname)); } if (m_first_propname != 0 && m_first_propname != m_table_propname && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("PROPNAME table offset does not match first occurrence of PROPNAME in strict mode - %s vs. %s")), m_table_propname, m_first_propname)); } if (m_first_propstring != 0 && m_first_propstring != m_table_propstring && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("PROPSTRING table offset does not match first occurrence of PROPSTRING in strict mode - %s vs. %s")), m_table_propstring, m_first_propstring)); } if (m_first_layername != 0 && m_first_layername != m_table_layername && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("LAYERNAME table offset does not match first occurrence of LAYERNAME in strict mode - %s vs. %s")), m_table_layername, m_first_layername)); } if (m_first_textstring != 0 && m_first_textstring != m_table_textstring && m_expect_strict_mode == 1) { warn (tl::sprintf (tl::to_string (tr ("TEXTSTRING table offset does not match first occurrence of TEXTSTRING in strict mode - %s vs. %s")), m_table_textstring, m_first_textstring)); } } void OASISReader::store_last_properties (db::PropertiesRepository &rep, db::PropertiesRepository::properties_set &properties, bool ignore_special) { if (! m_read_properties) { // All properties are ignored } else if (mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_s_gds_property_name_id) { if (mm_last_value_list.get ().size () != 2) { error (tl::to_string (tr ("S_GDS_PROPERTY must have a value list with exactly two elements"))); } properties.insert (std::make_pair (rep.prop_name_id (mm_last_value_list.get () [0]), mm_last_value_list.get () [1])); } else if (ignore_special && ! m_read_all_properties && mm_last_property_is_sprop.get ()) { // Special properties are not turned into user properties except S_GDS_PROPERTY. // This is mode is used for cells and layouts so the standard properties do not appear as user properties. // For shapes we need to keep the special ones since they may be forward-references S_GDS_PROPERTY names. } else if (mm_last_value_list.get ().size () == 0) { properties.insert (std::make_pair (mm_last_property_name.get (), tl::Variant ())); } else if (mm_last_value_list.get ().size () == 1) { properties.insert (std::make_pair (mm_last_property_name.get (), tl::Variant (mm_last_value_list.get () [0]))); } else if (mm_last_value_list.get ().size () > 1) { properties.insert (std::make_pair (mm_last_property_name.get (), tl::Variant (mm_last_value_list.get ().begin (), mm_last_value_list.get ().end ()))); } } std::pair OASISReader::read_element_properties (db::PropertiesRepository &rep, bool ignore_special) { db::PropertiesRepository::properties_set properties; mark_start_table (); while (true) { unsigned char m = get_byte (); if (m == 0 /*PAD*/) { // skip PAD. mark_start_table (); } else if (m == 34 /*CBLOCK*/) { unsigned int type = get_uint (); if (type != 0) { error (tl::sprintf (tl::to_string (tr ("Invalid CBLOCK compression type %d")), type)); } get_uint (); // uncomp-byte-count - not needed get_uint (); // comp-byte-count - not needed // put the stream into deflating mode m_stream.inflate (); } else if (m == 28 /*PROPERTY*/) { read_properties (rep); store_last_properties (rep, properties, ignore_special); mark_start_table (); } else if (m == 29 /*PROPERTY*/) { store_last_properties (rep, properties, ignore_special); mark_start_table (); } else { m_stream.unget (1); break; } } if (! properties.empty ()) { return std::make_pair (true, rep.properties_id (properties)); } else { return std::make_pair (false, 0); } } void OASISReader::read_properties (db::PropertiesRepository &rep) { unsigned char m = get_byte (); if (m & 0x04) { if (m & 0x02) { unsigned long id; get (id); std::map ::const_iterator cid = m_propnames.find (id); if (cid == m_propnames.end ()) { mm_last_property_name = rep.prop_name_id (tl::Variant (id, true /*dummy for id type*/)); m_propname_forward_references.insert (std::make_pair (id, mm_last_property_name.get ())); } else { mm_last_property_name = rep.prop_name_id (tl::Variant (cid->second)); } } else { if (m_expect_strict_mode == 1) { warn (tl::to_string (tr ("PROPERTY names must be references to PROPNAME ids in strict mode"))); } mm_last_property_name = rep.prop_name_id (tl::Variant (get_str ())); } } mm_last_property_is_sprop = ((m & 0x01) != 0); if (! (m & 0x08)) { unsigned long n = ((unsigned long) (m >> 4)) & 0x0f; if (n == 15) { get (n); } mm_last_value_list.get_non_const ().clear (); mm_last_value_list.get_non_const ().reserve (n); while (n > 0) { unsigned char t = get_byte (); if (t < 8) { m_stream.unget (1); double v = get_real (); if (m_read_properties) { mm_last_value_list.get_non_const ().push_back (tl::Variant (v)); } } else if (t == 8) { unsigned long l; get (l); if (m_read_properties) { mm_last_value_list.get_non_const ().push_back (tl::Variant (long (l))); } } else if (t == 9) { long l; get (l); if (m_read_properties) { mm_last_value_list.get_non_const ().push_back (tl::Variant (l)); } } else if (t == 10 || t == 11 || t == 12) { if (m_expect_strict_mode == 1) { warn (tl::to_string (tr ("PROPERTY strings must be references to PROPSTRING ids in strict mode"))); } if (m_read_properties) { mm_last_value_list.get_non_const ().push_back (tl::Variant (get_str ())); } else { get_str (); } } else if (t == 13 || t == 14 || t == 15) { unsigned long id; get (id); if (m_read_properties) { std::map ::const_iterator sid = m_propstrings.find (id); if (sid == m_propstrings.end ()) { m_propvalue_forward_references.insert (std::make_pair (id, std::string ())); mm_last_value_list.get_non_const ().push_back (tl::Variant (id, true /*dummy for id type*/)); } else { mm_last_value_list.get_non_const ().push_back (tl::Variant (sid->second)); } } } else { error (tl::sprintf (tl::to_string (tr ("Invalid property value type %d")), int (t))); } --n; } mm_last_value_list.set_initialized (); } } void OASISReader::read_pointlist (modal_variable > &pointlist, bool for_polygon) { unsigned int type = get_uint (); unsigned long n = 0; get (n); if (n == 0) { error (tl::to_string (tr ("Invalid point list: length is zero")).c_str ()); } pointlist.get_non_const ().clear (); if ((type == 0 || type == 1) && for_polygon) { // because for polygons, the pointlist will be closed implicitly pointlist.get_non_const ().reserve (n + 2); } else { pointlist.get_non_const ().reserve (n + 1); } pointlist.get_non_const ().push_back (db::Point ()); if (type == 0 || type == 1) { bool h = (type == 0); db::Point pos; for (unsigned long i = 0; i < n; ++i) { db::Coord d = get_coord (); if (h) { pos += db::Vector (d, 0); } else { pos += db::Vector (0, d); } h = ! h; pointlist.get_non_const ().push_back (pos); } // synthesize the last point for polygons if (for_polygon) { if ((n % 2) != 0) { warn (tl::to_string (tr ("Type 0 or 1 point list with odd number of points is illegal"))); } if (h) { pointlist.get_non_const ().push_back (db::Point (0, pos.y ())); } else { pointlist.get_non_const ().push_back (db::Point (pos.x (), 0)); } } } else if (type == 2) { db::Point pos; for (unsigned long i = 0; i < n; ++i) { pos += get_2delta (); pointlist.get_non_const ().push_back (pos); } } else if (type == 3) { db::Point pos; for (unsigned long i = 0; i < n; ++i) { pos += get_3delta (); pointlist.get_non_const ().push_back (pos); } } else if (type == 4) { db::Point pos; for (unsigned long i = 0; i < n; ++i) { pos += get_gdelta (); pointlist.get_non_const ().push_back (pos); } } else if (type == 5) { db::Point pos; db::Vector delta; for (unsigned long i = 0; i < n; ++i) { delta += get_gdelta (); pos += delta; pointlist.get_non_const ().push_back (pos); } } else { error (tl::sprintf (tl::to_string (tr ("Invalid point list type %d")), type)); } pointlist.set_initialized (); } bool OASISReader::read_repetition () { unsigned char type = get_uint (); if (type == 0) { // reuse modal variable } else if (type == 1) { unsigned long nx = 0, ny = 0; get (nx); get (ny); db::Coord dx = get_ucoord (); db::Coord dy = get_ucoord (); mm_repetition = new RegularRepetition (db::Vector (dx, 0), db::Vector (0, dy), dx == 0 ? 1 : nx + 2, dy == 0 ? 1 : ny + 2); } else if (type == 2) { unsigned long nx = 0; get (nx); db::Coord dx = get_ucoord (); mm_repetition = new RegularRepetition (db::Vector (dx, 0), db::Vector (0, 0), dx == 0 ? 1 : nx + 2, 1); } else if (type == 3) { unsigned long ny = 0; get (ny); db::Coord dy = get_ucoord (); mm_repetition = new RegularRepetition (db::Vector (0, 0), db::Vector (0, dy), 1, dy == 0 ? 1 : ny + 2); } else if (type == 4 || type == 5) { IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; unsigned long n = 0; get (n); unsigned long lgrid = 1; if (type == 5) { get (lgrid); } rep->reserve (n + 1); db::Coord x = 0; for (unsigned long i = 0; i <= n; ++i) { m_progress.set (m_stream.pos ()); db::Coord d = get_ucoord (lgrid); if (d != 0) { x += d; rep->push_back (db::Vector (x, 0)); } } } else if (type == 6 || type == 7) { IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; unsigned long n = 0; get (n); unsigned long lgrid = 1; if (type == 7) { get (lgrid); } rep->reserve (n + 1); db::Coord y = 0; for (unsigned long i = 0; i <= n; ++i) { m_progress.set (m_stream.pos ()); db::Coord d = get_ucoord (lgrid); if (d != 0) { y += d; rep->push_back (db::Vector (0, y)); } } } else if (type == 8) { unsigned long n = 0, m = 0; get (n); get (m); db::Vector dn = get_gdelta (); db::Vector dm = get_gdelta (); mm_repetition = new RegularRepetition (dn, dm, dn == db::Vector () ? 1 : n + 2, dm == db::Vector () ? 1 : m + 2); } else if (type == 9) { unsigned long n = 0; get (n); db::Vector dn = get_gdelta (); mm_repetition = new RegularRepetition (dn, db::Vector (0, 0), dn == db::Vector () ? 1 : n + 2, 1); } else if (type == 10 || type == 11) { IrregularRepetition *rep = new IrregularRepetition (); mm_repetition = rep; unsigned long n = 0; get (n); unsigned long grid = 1; if (type == 11) { get (grid); } rep->reserve (n + 1); db::Vector p; for (unsigned long i = 0; i <= n; ++i) { m_progress.set (m_stream.pos ()); db::Vector d = get_gdelta (grid); if (d != db::Vector ()) { p += d; rep->push_back (p); } } } else { error (tl::sprintf (tl::to_string (tr ("Invalid repetition type %d")), type)); } return mm_repetition.get ().size () > 1; } void OASISReader::do_read_placement (unsigned char r, bool xy_absolute, db::Layout &layout, tl::vector &instances, tl::vector &instances_with_props) { unsigned char m = get_byte (); // locate cell if (m & 0x80) { if (m & 0x40) { // cell by id unsigned long id; get (id); mm_placement_cell = cell_for_instance (layout, id); } else { // cell by name std::string name; get_str (name); mm_placement_cell = cell_for_instance (layout, name); } } double mag = 1.0; bool mag_set = false; double angle_deg = 0.0; // only meaningful if angle < 0 int angle = 0; bool mirror = false; if (r == 18) { if (m & 0x04) { mag = get_real (); if (fabs (mag - 1.0) > 1e-6) { mag_set = true; } } if (m & 0x02) { angle_deg = get_real (); double a = angle_deg / 90.0; if (a < -4 || a > 4) { warn (tl::sprintf (tl::to_string (tr ("Invalid rotation angle (%g is less than -360 or larger than 360)")), angle_deg)); } angle = int (a < 0 ? (a - 0.5) : (a + 0.5)); if (fabs (double (angle) - a) > 1e-6) { angle = -1; // indicates arbitrary orientation. Take angle_deg instead } else { if (angle < 0) { angle += ((4 - 1) - angle) & ~(4 - 1); } angle = angle % 4; } } } else { angle = ((m & 0x06) >> 1); } mirror = (m & 0x01) != 0; if (m & 0x20) { db::Coord x; get (x); if (xy_absolute) { mm_placement_x = x; } else { mm_placement_x = x + mm_placement_x.get (); } } if (m & 0x10) { db::Coord y; get (y); if (xy_absolute) { mm_placement_y = y; } else { mm_placement_y = y + mm_placement_y.get (); } } db::Vector pos (mm_placement_x.get (), mm_placement_y.get ()); const std::vector *points = 0; if ((m & 0x8) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); db::Vector a, b; size_t na, nb; if (mm_repetition.get ().is_regular (a, b, na, nb)) { db::CellInstArray inst; if (mag_set || angle < 0) { inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::ICplxTrans (mag, angle_deg, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } else { inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (angle, mirror, pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb); } if (pp.first) { instances_with_props.push_back (db::CellInstArrayWithProperties (inst, pp.second)); } else { instances.push_back (inst); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { db::CellInstArray inst; if (mag_set || angle < 0) { db::ICplxTrans ct (mag, angle_deg, mirror, pos); db::CellInstArray::iterated_complex_array_type array (ct.rcos (), ct.mag ()); array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (ct), layout.array_repository ().insert (array)); } else { db::CellInstArray::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (angle, mirror, pos), layout.array_repository ().insert (array)); } if (pp.first) { instances_with_props.push_back (db::CellInstArrayWithProperties (inst, pp.second)); } else { instances.push_back (inst); } } else { RepetitionIterator p = mm_repetition.get ().begin (); while (! p.at_end ()) { db::CellInstArray inst; if (mag_set || angle < 0) { inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::ICplxTrans (mag, angle_deg, mirror, pos + *p)); } else { inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (angle, mirror, pos + *p)); } if (pp.first) { instances_with_props.push_back (db::CellInstArrayWithProperties (inst, pp.second)); } else { instances.push_back (inst); } ++p; } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); db::CellInstArray inst; if (mag_set || angle < 0) { inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::ICplxTrans (mag, angle_deg, mirror, pos)); } else { inst = db::CellInstArray (db::CellInst (mm_placement_cell.get ()), db::Trans (angle, mirror, pos)); } if (pp.first) { instances_with_props.push_back (db::CellInstArrayWithProperties (inst, pp.second)); } else { instances.push_back (inst); } } } void OASISReader::do_read_text (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); if (m & 0x40) { if (m & 0x20) { unsigned long id; get (id); if (m_text_forward_references.find (id) != m_text_forward_references.end ()) { mm_text_string.reset (); mm_text_string_id = id; } else { std::map ::const_iterator tid = m_textstrings.find (id); if (tid == m_textstrings.end ()) { mm_text_string.reset (); mm_text_string_id = id; const db::StringRef *string_ref = layout.string_repository ().create_string_ref (); m_text_forward_references.insert (std::make_pair (id, string_ref)); } else { mm_text_string = tid->second; } } } else { if (m_expect_strict_mode == 1) { warn (tl::to_string (tr ("TEXT strings must be references to TEXTSTRING ids in strict mode"))); } mm_text_string = get_str (); } } if (m & 0x1) { mm_textlayer = get_uint (); } if (m & 0x2) { mm_texttype = get_uint (); } if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_text_x = x; } else { mm_text_x = x + mm_text_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_text_y = y; } else { mm_text_y = y + mm_text_y.get (); } } db::Vector pos (mm_text_x.get (), mm_text_y.get ()); std::pair ll (false, 0); if (m_read_texts) { ll = open_dl (layout, LDPair (mm_textlayer.get (), mm_texttype.get ())); } if ((m & 0x4) && read_repetition ()) { // TODO: should not read properties if layer is not enabled! std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { db::Text text; if (mm_text_string_id.is_set ()) { text = db::Text (m_text_forward_references.find (mm_text_string_id.get ())->second, db::Trans ()); } else { text = db::Text (mm_text_string.get (), db::Trans ()); } db::Cell &cell = layout.cell (cell_index); const std::vector *points = 0; // If the repetition is a regular one, convert the repetition into // a shape array db::Vector a, b; size_t na, nb; if (! layout.is_editable () && mm_repetition.get ().is_regular (a, b, na, nb)) { db::TextPtr text_ptr (text, layout.shape_repository ()); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb)); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { db::TextPtr text_ptr (text, layout.shape_repository ()); // Create an iterated text array db::Shape::text_ptr_array_type::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::text_ptr_array_type (text_ptr, db::Disp (pos), layout.array_repository ().insert (array))); } } else { RepetitionIterator p = mm_repetition.get ().begin (); db::TextRef text_ref (text, layout.shape_repository ()); while (! p.at_end ()) { if (pp.first) { cell.shapes (ll.second).insert (db::TextRefWithProperties (text_ref.transformed (db::Disp (pos + *p)), pp.second)); } else { cell.shapes (ll.second).insert (text_ref.transformed (db::Disp (pos + *p))); } ++p; } } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { db::Text text; if (mm_text_string_id.is_set ()) { text = db::Text (m_text_forward_references.find (mm_text_string_id.get ())->second, db::Trans (pos)); } else { text = db::Text (mm_text_string.get (), db::Trans (pos)); } if (pp.first) { layout.cell (cell_index).shapes (ll.second).insert (db::TextRefWithProperties (db::TextRef (text, layout.shape_repository ()), pp.second)); } else { layout.cell (cell_index).shapes (ll.second).insert (db::TextRef (text, layout.shape_repository ())); } } } } void OASISReader::do_read_rectangle (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); if (m & 0x1) { mm_layer = get_uint (); } if (m & 0x2) { mm_datatype = get_uint (); } if (m & 0x40) { mm_geometry_w = get_ucoord_as_distance (); } if (m & 0x80) { mm_geometry_h = mm_geometry_w; // TODO: really? } else { if (m & 0x20) { mm_geometry_h = get_ucoord_as_distance (); } } if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_geometry_x = x; } else { mm_geometry_x = x + mm_geometry_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_geometry_y = y; } else { mm_geometry_y = y + mm_geometry_y.get (); } } db::Box box (db::Point (mm_geometry_x.get (), mm_geometry_y.get ()), db::Point (mm_geometry_x.get () + mm_geometry_w.get (), mm_geometry_y.get () + mm_geometry_h.get ())); std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); if ((m & 0x4) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { db::Cell &cell = layout.cell (cell_index); const std::vector *points = 0; // If the repetition is a regular one, convert the repetition into // a box array db::Vector a, b; size_t na, nb; if (! layout.is_editable () && mm_repetition.get ().is_regular (a, b, na, nb)) { // Create a box array if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb)); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { // Create an iterated box array db::Shape::box_array_type::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository ().insert (array)), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::box_array_type (box, db::UnitTrans (), layout.array_repository ().insert (array))); } } else { // convert the OASIS record into the rectangle one by one. RepetitionIterator p = mm_repetition.get ().begin (); while (! p.at_end ()) { if (pp.first) { cell.shapes (ll.second).insert (db::BoxWithProperties (box.moved (*p), pp.second)); } else { cell.shapes (ll.second).insert (box.moved (*p)); } ++p; } } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { db::Cell &cell = layout.cell (cell_index); if (pp.first) { cell.shapes (ll.second).insert (db::BoxWithProperties (box, pp.second)); } else { cell.shapes (ll.second).insert (box); } } } } void OASISReader::do_read_polygon (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); if (m & 0x1) { mm_layer = get_uint (); } if (m & 0x2) { mm_datatype = get_uint (); } if (m & 0x20) { read_pointlist (mm_polygon_point_list, true); } if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_geometry_x = x; } else { mm_geometry_x = x + mm_geometry_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_geometry_y = y; } else { mm_geometry_y = y + mm_geometry_y.get (); } } db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); if ((m & 0x4) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { db::Cell &cell = layout.cell (cell_index); if (mm_polygon_point_list.get ().size () < 3) { warn (tl::to_string (tr ("POLYGON with less than 3 points ignored"))); } else { // convert the OASIS record into the polygon. db::SimplePolygon poly; poly.assign_hull (mm_polygon_point_list.get ().begin (), mm_polygon_point_list.get ().end (), false /*no compression*/); const std::vector *points = 0; // If the repetition is a regular one, convert the repetition into // a shape array db::Vector a, b; size_t na, nb; if (! layout.is_editable () && mm_repetition.get ().is_regular (a, b, na, nb)) { // creating a SimplePolygonPtr is most efficient with a normalized polygon because no displacement is provided db::Vector d (poly.box ().lower_left () - db::Point ()); poly.move (-d); db::SimplePolygonPtr poly_ptr (poly, layout.shape_repository ()); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties > (db::array (poly_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb), pp.second)); } else { cell.shapes (ll.second).insert (db::array (poly_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb)); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { db::Vector d (poly.box ().lower_left () - db::Point ()); poly.move (-d); db::SimplePolygonPtr poly_ptr (poly, layout.shape_repository ()); // Create an iterated simple polygon array db::Shape::simple_polygon_ptr_array_type::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array))); } } else { db::SimplePolygonRef poly_ref (poly, layout.shape_repository ()); RepetitionIterator p = mm_repetition.get ().begin (); while (! p.at_end ()) { if (pp.first) { cell.shapes (ll.second).insert (db::SimplePolygonRefWithProperties (poly_ref.transformed (db::Disp (pos + *p)), pp.second)); } else { cell.shapes (ll.second).insert (poly_ref.transformed (db::Disp (pos + *p))); } ++p; } } } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { if (mm_polygon_point_list.get ().size () < 3) { warn (tl::to_string (tr ("POLYGON with less than 3 points ignored"))); } else { // convert the OASIS record into the polygon. db::SimplePolygon poly; poly.assign_hull (mm_polygon_point_list.get ().begin (), mm_polygon_point_list.get ().end (), false /*no compression*/); db::SimplePolygonRef poly_ref (poly, layout.shape_repository ()); if (pp.first) { layout.cell (cell_index).shapes (ll.second).insert (db::SimplePolygonRefWithProperties (poly_ref.transformed (db::Disp (pos)), pp.second)); } else { layout.cell (cell_index).shapes (ll.second).insert (poly_ref.transformed (db::Disp (pos))); } } } } } void OASISReader::do_read_path (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); if (m & 0x1) { mm_layer = get_uint (); } if (m & 0x2) { mm_datatype = get_uint (); } if (m & 0x40) { mm_path_halfwidth = get_ucoord_as_distance (); } if (m & 0x80) { unsigned int e = get_uint (); if ((e & 0x0c) == 0x0c) { mm_path_start_extension = get_coord (); } else if ((e & 0x0c) == 0x04) { mm_path_start_extension = 0; // TODO: is setting the start extension modal variable correct here? } else if ((e & 0x0c) == 0x08) { mm_path_start_extension = mm_path_halfwidth.get (); // TODO: is setting the start extension modal variable correct here? } if ((e & 0x03) == 0x03) { mm_path_end_extension = get_coord (); } else if ((e & 0x03) == 0x01) { mm_path_end_extension = 0; // TODO: is setting the start extension modal variable correct here? } else if ((e & 0x03) == 0x02) { mm_path_end_extension = mm_path_halfwidth.get (); // TODO: is setting the start extension modal variable correct here? } } if (m & 0x20) { read_pointlist (mm_path_point_list, false); } if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_geometry_x = x; } else { mm_geometry_x = x + mm_geometry_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_geometry_y = y; } else { mm_geometry_y = y + mm_geometry_y.get (); } } db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); if ((m & 0x4) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { if (mm_path_point_list.get ().size () < 2) { warn (tl::to_string (tr ("POLYGON with less than 2 points ignored"))); } else { // convert the OASIS record into the path. db::Path path; path.width (2 * mm_path_halfwidth.get ()); path.extensions (mm_path_start_extension.get (), mm_path_end_extension.get ()); path.assign (mm_path_point_list.get ().begin (), mm_path_point_list.get ().end ()); db::Cell &cell = layout.cell (cell_index); const std::vector *points = 0; // If the repetition is a regular one, convert the repetition into // a shape array db::Vector a, b; size_t na, nb; if (! layout.is_editable () && mm_repetition.get ().is_regular (a, b, na, nb)) { // creating a PathPtr is most efficient with a normalized path because no displacement is provided db::Vector d (*path.begin ()); path.move (-d); db::PathPtr path_ptr (path, layout.shape_repository ()); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties > (db::array (path_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb), pp.second)); } else { cell.shapes (ll.second).insert (db::array (path_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb)); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { db::Vector d (*path.begin () - db::Point ()); path.move (-d); db::PathPtr path_ptr (path, layout.shape_repository ()); // Create an iterated simple polygon array db::Shape::path_ptr_array_type::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::path_ptr_array_type (path_ptr, db::Disp (d + pos), layout.array_repository ().insert (array))); } } else { db::PathRef path_ref (path, layout.shape_repository ()); RepetitionIterator p = mm_repetition.get ().begin (); while (! p.at_end ()) { if (pp.first) { cell.shapes (ll.second).insert (db::PathRefWithProperties (path_ref.transformed (db::Disp (pos + *p)), pp.second)); } else { cell.shapes (ll.second).insert (path_ref.transformed (db::Disp (pos + *p))); } ++p; } } } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { if (mm_path_point_list.get ().size () < 2) { warn (tl::to_string (tr ("PATH with less than 2 points ignored"))); } else { // convert the OASIS record into the path. db::Path path; path.width (2 * mm_path_halfwidth.get ()); path.extensions (mm_path_start_extension.get (), mm_path_end_extension.get ()); path.assign (mm_path_point_list.get ().begin (), mm_path_point_list.get ().end ()); db::PathRef path_ref (path, layout.shape_repository ()); if (pp.first) { layout.cell (cell_index).shapes (ll.second).insert (db::PathRefWithProperties (path_ref.transformed (db::Disp (pos)), pp.second)); } else { layout.cell (cell_index).shapes (ll.second).insert (path_ref.transformed (db::Disp (pos))); } } } } } void OASISReader::do_read_trapezoid (unsigned char r, bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); if (m & 0x1) { mm_layer = get_uint (); } if (m & 0x2) { mm_datatype = get_uint (); } if (m & 0x40) { mm_geometry_w = get_ucoord_as_distance (); } if (m & 0x20) { mm_geometry_h = get_ucoord_as_distance (); } db::Coord delta_a = 0, delta_b = 0; if (r == 23 || r == 24) { delta_a = get_coord (); } if (r == 23 || r == 25) { delta_b = get_coord (); } if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_geometry_x = x; } else { mm_geometry_x = x + mm_geometry_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_geometry_y = y; } else { mm_geometry_y = y + mm_geometry_y.get (); } } db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); db::Point pts [4]; if (m & 0x80) { // vertically pts [0] = db::Point (0, std::max (delta_a, db::Coord (0))); pts [1] = db::Point (0, mm_geometry_h.get () + std::min (delta_b, db::Coord (0))); pts [2] = db::Point (mm_geometry_w.get (), mm_geometry_h.get () - std::max (delta_b, db::Coord (0))); pts [3] = db::Point (mm_geometry_w.get (), -std::min (delta_a, db::Coord (0))); } else { // horizontally pts [0] = db::Point (std::max (delta_a, db::Coord (0)), mm_geometry_h.get ()); pts [1] = db::Point (mm_geometry_w.get () + std::min (delta_b, db::Coord (0)), mm_geometry_h.get ()); pts [2] = db::Point (mm_geometry_w.get () - std::max (delta_b, db::Coord (0)), db::Coord (0)); pts [3] = db::Point (-std::min (delta_a, db::Coord (0)), db::Coord (0)); } if ((m & 0x4) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { // convert the OASIS record into the polygon. db::SimplePolygon poly; poly.assign_hull (pts, pts + 4, false /*no compression*/); db::Cell &cell = layout.cell (cell_index); const std::vector *points = 0; // If the repetition is a regular one, convert the repetition into // a shape array db::Vector a, b; size_t na, nb; if (! layout.is_editable () && mm_repetition.get ().is_regular (a, b, na, nb)) { // creating a SimplePolygonPtr is most efficient with a normalized polygon because no displacement is provided db::Vector d (poly.box ().lower_left ()); poly.move (-d); db::SimplePolygonPtr poly_ptr (poly, layout.shape_repository ()); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties > (db::array (poly_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb), pp.second)); } else { cell.shapes (ll.second).insert (db::array (poly_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb)); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { db::Vector d (poly.box ().lower_left () - db::Point ()); poly.move (-d); db::SimplePolygonPtr poly_ptr (poly, layout.shape_repository ()); // Create an iterated simple polygon array db::Shape::simple_polygon_ptr_array_type::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array))); } } else { db::SimplePolygonRef poly_ref (poly, layout.shape_repository ()); RepetitionIterator p = mm_repetition.get ().begin (); while (! p.at_end ()) { if (pp.first) { cell.shapes (ll.second).insert (db::SimplePolygonRefWithProperties (poly_ref.transformed (db::Disp (pos + *p)), pp.second)); } else { cell.shapes (ll.second).insert (poly_ref.transformed (db::Disp (pos + *p))); } ++p; } } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { // convert the OASIS record into the polygon. db::SimplePolygon poly; poly.assign_hull (pts, pts + 4, false /*no compression*/); db::SimplePolygonRef poly_ref (poly, layout.shape_repository ()); if (pp.first) { layout.cell (cell_index).shapes (ll.second).insert (SimplePolygonRefWithProperties (poly_ref.transformed (db::Disp (pos)), pp.second)); } else { layout.cell (cell_index).shapes (ll.second).insert (poly_ref.transformed (db::Disp (pos))); } } } } void OASISReader::do_read_ctrapezoid (bool xy_absolute,db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); if (m & 0x1) { mm_layer = get_uint (); } if (m & 0x2) { mm_datatype = get_uint (); } if (m & 0x80) { mm_ctrapezoid_type = get_uint (); } if (m & 0x40) { mm_geometry_w = get_ucoord_as_distance (); } if (m & 0x20) { mm_geometry_h = get_ucoord_as_distance (); } if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_geometry_x = x; } else { mm_geometry_x = x + mm_geometry_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_geometry_y = y; } else { mm_geometry_y = y + mm_geometry_y.get (); } } db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); db::Point pts [4]; static db::Coord ctraps_table[][4][4] = { // type 0 { { 0, 0, 0, 0 }, // x=0*w+0*h, y=0*w+0*h ... { 0, 0, 0, 1 }, { 1, -1, 0, 1 }, { 1, 0, 0, 0 } }, // type 1 { { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 2 { { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 3 { { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 4 { { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, -1, 0, 1 }, { 1, 0, 0, 0 } }, // type 5 { { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 6 { { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 1, 0, 0, 1 }, { 1, -1, 0, 0 } }, // type 7 { { 0, 1, 0, 0 }, { 0, 0, 0, 1 }, { 1, -1, 0, 1 }, { 1, 0, 0, 0 } }, // type 8 { { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 9 { { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 10 { { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 11 { { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 12 { { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 1, 0 } }, // type 13 { { 0, 0, 1, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 14 { { 0, 0, 0, 0 }, { 0, 0, -1, 1 }, { 1, 0, 0, 1 }, { 1, 0, 1, 0 } }, // type 15 { { 0, 0, 1, 0 }, { 0, 0, 0, 1 }, { 1, 0, -1, 1 }, { 1, 0, 0, 0 } }, // type 16 { { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 17 { { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 18 { { 0, 0, 0, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 0, 0 } }, // type 19 { { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 }, { 0, 0, 1, 0 } }, // type 20 { { 0, 0, 0, 0 }, { 0, 1, 0, 1 }, { 0, 2, 0, 0 }, { 0, 0, 0, 0 } }, // type 21 { { 0, 0, 0, 1 }, { 0, 2, 0, 1 }, { 0, 1, 0, 0 }, { 0, 0, 0, 1 } }, // type 22 { { 0, 0, 0, 0 }, { 0, 0, 2, 0 }, { 1, 0, 1, 0 }, { 0, 0, 0, 0 } }, // type 23 { { 1, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 2, 0 }, { 1, 0, 0, 0 } }, // type 24 { { 0, 0, 0, 0 }, { 0, 0, 0, 1 }, { 1, 0, 0, 1 }, { 1, 0, 0, 0 } }, // type 25 { { 0, 0, 0, 0 }, { 0, 0, 1, 0 }, { 1, 0, 1, 0 }, { 1, 0, 0, 0 } }, }; if (mm_ctrapezoid_type.get () >= sizeof (ctraps_table) / sizeof (ctraps_table [0])) { error (tl::sprintf (tl::to_string (tr ("Invalid CTRAPEZOID type %d")), int (mm_ctrapezoid_type.get ()))); } db::Coord w = 0, h = 0; for (unsigned i = 0; i < 4; ++i) { db::Coord *m = ctraps_table [mm_ctrapezoid_type.get ()][i]; db::Coord x = 0; if (m[0] != 0) x += m[0] * mm_geometry_w.get (); if (m[1] != 0) x += m[1] * mm_geometry_h.get (); db::Coord y = 0; if (m[2] != 0) y += m[2] * mm_geometry_w.get (); if (m[3] != 0) y += m[3] * mm_geometry_h.get (); pts [i] = db::Point (x, y); if (x > w) w = x; if (y > h) h = y; } // set modal variables to the bbox of the shape mm_geometry_w = w; mm_geometry_h = h; unsigned int npts = 4; if (pts [npts - 1] == pts [0]) { --npts; } if ((m & 0x4) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { // convert the OASIS record into the polygon. db::SimplePolygon poly; poly.assign_hull (pts, pts + npts, false /*no compression*/); db::Cell &cell = layout.cell (cell_index); const std::vector *points = 0; // If the repetition is a regular one, convert the repetition into // a shape array db::Vector a, b; size_t na, nb; if (! layout.is_editable () && mm_repetition.get ().is_regular (a, b, na, nb)) { db::Vector d (poly.box ().lower_left () - db::Point ()); poly.move (-d); db::SimplePolygonPtr poly_ptr (poly, layout.shape_repository ()); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties > (db::array (poly_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb), pp.second)); } else { cell.shapes (ll.second).insert (db::array (poly_ptr, db::Disp (d + pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb)); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { db::Vector d (poly.box ().lower_left () - db::Point ()); poly.move (-d); db::SimplePolygonPtr poly_ptr (poly, layout.shape_repository ()); // Create an iterated simple polygon array db::Shape::simple_polygon_ptr_array_type::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array)), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::simple_polygon_ptr_array_type (poly_ptr, db::Disp (d + pos), layout.array_repository ().insert (array))); } } else { db::SimplePolygonRef poly_ref (poly, layout.shape_repository ()); RepetitionIterator p = mm_repetition.get ().begin (); while (! p.at_end ()) { if (pp.first) { cell.shapes (ll.second).insert (db::SimplePolygonRefWithProperties (poly_ref.transformed (db::Disp (pos + *p)), pp.second)); } else { cell.shapes (ll.second).insert (poly_ref.transformed (db::Disp (pos + *p))); } ++p; } } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { // convert the OASIS record into the polygon. db::SimplePolygon poly; poly.assign_hull (pts, pts + npts, false /*no compression*/); db::SimplePolygonRef poly_ref (poly, layout.shape_repository ()); if (pp.first) { layout.cell (cell_index).shapes (ll.second).insert (db::SimplePolygonRefWithProperties (poly_ref.transformed (db::Disp (pos)), pp.second)); } else { layout.cell (cell_index).shapes (ll.second).insert (poly_ref.transformed (db::Disp (pos))); } } } } void OASISReader::do_read_circle (bool xy_absolute, db::cell_index_type cell_index, db::Layout &layout) { unsigned char m = get_byte (); if (m & 0x1) { mm_layer = get_uint (); } if (m & 0x2) { mm_datatype = get_uint (); } if (m & 0x20) { mm_circle_radius = get_ucoord_as_distance (); } if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_geometry_x = x; } else { mm_geometry_x = x + mm_geometry_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_geometry_y = y; } else { mm_geometry_y = y + mm_geometry_y.get (); } } db::Vector pos (mm_geometry_x.get (), mm_geometry_y.get ()); std::pair ll = open_dl (layout, LDPair (mm_layer.get (), mm_datatype.get ())); // ignore this circle if the radius is zero if (mm_circle_radius.get () <= 0) { ll.first = false; } if ((m & 0x4) && read_repetition ()) { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { // convert the OASIS circle into a single-point path. db::Path path; path.width (2 * mm_circle_radius.get ()); path.extensions (mm_circle_radius.get (), mm_circle_radius.get ()); path.round (true); db::Point p0 (0, 0); path.assign (&p0, &p0 + 1); db::Cell &cell = layout.cell (cell_index); const std::vector *points = 0; // If the repetition is a regular one, convert the repetition into // a shape array db::Vector a, b; size_t na, nb; if (! layout.is_editable () && mm_repetition.get ().is_regular (a, b, na, nb)) { // creating a PathPtr is most efficient with a normalized path because no displacement is provided db::PathPtr path_ptr (path, layout.shape_repository ()); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties > (db::array (path_ptr, db::Disp (pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb), pp.second)); } else { cell.shapes (ll.second).insert (db::array (path_ptr, db::Disp (pos), layout.array_repository (), a, b, (unsigned long) na, (unsigned long) nb)); } } else if (! layout.is_editable () && (points = mm_repetition.get ().is_iterated ()) != 0) { db::PathPtr path_ptr (path, layout.shape_repository ()); // Create an iterated simple polygon array db::Shape::path_ptr_array_type::iterated_array_type array; array.reserve (points->size () + 1); array.insert (db::Vector ()); array.insert (points->begin (), points->end ()); array.sort (); if (pp.first) { cell.shapes (ll.second).insert (db::object_with_properties (db::Shape::path_ptr_array_type (path_ptr, db::Disp (pos), layout.array_repository ().insert (array)), pp.second)); } else { cell.shapes (ll.second).insert (db::Shape::path_ptr_array_type (path_ptr, db::Disp (pos), layout.array_repository ().insert (array))); } } else { db::PathRef path_ref (path, layout.shape_repository ()); RepetitionIterator p = mm_repetition.get ().begin (); while (! p.at_end ()) { if (pp.first) { cell.shapes (ll.second).insert (db::PathRefWithProperties (path_ref.transformed (db::Disp (pos + *p)), pp.second)); } else { cell.shapes (ll.second).insert (path_ref.transformed (db::Disp (pos + *p))); } ++p; } } } } else { std::pair pp = read_element_properties (layout.properties_repository (), false); if (ll.first) { // convert the OASIS circle into a single-point path. db::Path path; path.width (2 * mm_circle_radius.get ()); path.extensions (mm_circle_radius.get (), mm_circle_radius.get ()); path.round (true); db::Point p0 (0, 0); path.assign (&p0, &p0 + 1); db::PathRef path_ref (path, layout.shape_repository ()); if (pp.first) { layout.cell (cell_index).shapes (ll.second).insert (db::PathRefWithProperties (path_ref.transformed (db::Disp (pos)), pp.second)); } else { layout.cell (cell_index).shapes (ll.second).insert (path_ref.transformed (db::Disp (pos))); } } } } void OASISReader::reset_modal_variables () { // reset modal variables mm_repetition.reset (); mm_placement_cell.reset (); mm_placement_x = 0; mm_placement_y = 0; mm_layer.reset (); mm_datatype.reset (); mm_textlayer.reset (); mm_texttype.reset (); mm_text_x = 0; mm_text_y = 0; mm_text_string.reset (); mm_text_string_id.reset (); mm_geometry_x = 0; mm_geometry_y = 0; mm_geometry_w.reset (); mm_geometry_h.reset (); mm_polygon_point_list.reset (); mm_path_halfwidth.reset (); mm_path_start_extension.reset (); mm_path_end_extension.reset (); mm_path_point_list.reset (); mm_ctrapezoid_type.reset (); mm_circle_radius.reset (); mm_last_property_name.reset (); mm_last_property_is_sprop.reset (); mm_last_value_list.reset (); } void OASISReader::do_read_cell (db::cell_index_type cell_index, db::Layout &layout) { // clears current instance list m_instances.clear (); m_instances_with_props.clear (); m_progress.set (m_stream.pos ()); bool xy_absolute = true; bool has_context = false; std::vector context_strings; db::PropertiesRepository::properties_set cell_properties; // read next record while (true) { m_progress.set (m_stream.pos ()); unsigned char r = get_byte (); if (r == 0 /*PAD*/) { // simply skip. mark_start_table (); } else if (r == 15 /*XYABSOLUTE*/) { // switch to absolute mode xy_absolute = true; mark_start_table (); } else if (r == 16 /*XYRELATIVE*/) { // switch to relative mode xy_absolute = false; mark_start_table (); } else if (r == 17 || r == 18 /*PLACEMENT*/) { do_read_placement (r, xy_absolute, layout, m_instances, m_instances_with_props); } else if (r == 19 /*TEXT*/) { do_read_text (xy_absolute, cell_index, layout); } else if (r == 20 /*RECTANGLE*/) { do_read_rectangle (xy_absolute, cell_index, layout); } else if (r == 21 /*POLYGON*/) { do_read_polygon (xy_absolute, cell_index, layout); } else if (r == 22 /*PATH*/) { do_read_path (xy_absolute, cell_index, layout); } else if (r == 23 || r == 24 || r == 25 /*TRAPEZOID*/) { do_read_trapezoid (r, xy_absolute, cell_index, layout); } else if (r == 26 /*CTRAPEZOID*/) { do_read_ctrapezoid (xy_absolute, cell_index, layout); } else if (r == 27 /*CIRCLE*/) { do_read_circle (xy_absolute, cell_index, layout); } else if (r == 28 || r == 29 /*PROPERTY*/) { if (r == 28 /*PROPERTY*/) { read_properties (layout.properties_repository ()); } if (! mm_last_property_is_sprop.get () && mm_last_property_name.get () == m_klayout_context_property_name_id) { has_context = true; context_strings.reserve (mm_last_value_list.get ().size ()); for (std::vector::const_iterator v = mm_last_value_list.get ().begin (); v != mm_last_value_list.get ().end (); ++v) { context_strings.push_back (v->to_string ()); } } else { // store cell properties store_last_properties (layout.properties_repository (), cell_properties, true); } mark_start_table (); } else if (r == 32 /*XELEMENT*/) { // read over get_ulong (); get_str (); read_element_properties (layout.properties_repository (), true); } else if (r == 33 /*XGEOMETRY*/) { // read over. unsigned char m = get_byte (); get_ulong (); if (m & 0x1) { mm_layer = get_uint (); } if (m & 0x2) { mm_datatype = get_uint (); } // data payload: get_str (); if (m & 0x10) { db::Coord x; get (x); if (xy_absolute) { mm_geometry_x = x; } else { mm_geometry_x = x + mm_geometry_x.get (); } } if (m & 0x8) { db::Coord y; get (y); if (xy_absolute) { mm_geometry_y = y; } else { mm_geometry_y = y + mm_geometry_y.get (); } } if ((m & 0x4) && read_repetition ()) { // later: handle XGEOMETRY with repetition } read_element_properties (layout.properties_repository (), true); } else if (r == 34 /*CBLOCK*/) { unsigned int type = get_uint (); if (type != 0) { error (tl::sprintf (tl::to_string (tr ("Invalid CBLOCK compression type %d")), type)); } get_uint (); // uncomp-byte-count - not needed get_uint (); // comp-byte-count - not needed // put the stream into deflating mode m_stream.inflate (); } else { // put the byte back into the stream m_stream.unget (1); break; } } if (! cell_properties.empty ()) { layout.cell (cell_index).prop_id (layout.properties_repository ().properties_id (cell_properties)); } // insert all instances collected (inserting them once is // more effective than doing this every time) if (! m_instances.empty ()) { layout.cell (cell_index).insert (m_instances.begin (), m_instances.end ()); // clear immediately, because if the cell is cleared before the instances are deleted, the // array pointers (living in the repository) may no longer be valid m_instances.clear (); } if (! m_instances_with_props.empty ()) { layout.cell (cell_index).insert (m_instances_with_props.begin (), m_instances_with_props.end ()); // see above. m_instances_with_props.clear (); } // Restore proxy cell (link to PCell or Library) if (has_context) { CommonReaderLayerMapping layer_mapping (this, &layout); layout.recover_proxy_as (cell_index, context_strings.begin (), context_strings.end (), &layer_mapping); } m_cellname = ""; } }