Xorn
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
plainread.py
Go to the documentation of this file.
1 # xorn.geda - Python library for manipulating gEDA files
2 # Copyright (C) 1998-2010 Ales Hvezda
3 # Copyright (C) 1998-2010 gEDA Contributors (see ChangeLog for details)
4 # Copyright (C) 2013-2016 Roland Lutz
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software Foundation,
18 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 
20 ## \namespace xorn.geda.plainread
21 ## Reading gEDA schematic/symbol files.
22 #
23 # The gEDA file format is a space separated list of characters and
24 # numbers in plain ASCII. The meaning of each item is described in
25 # the file format documentation which can be found in \c
26 # doc/geda/file_format_spec or
27 # [here](http://wiki.geda-project.org/geda:file_format_spec).
28 
29 import codecs, os, sys
30 from gettext import gettext as _
31 import xorn.base64
32 import xorn.proxy
33 import xorn.storage
34 import xorn.geda.attrib
35 import xorn.geda.read
36 import xorn.geda.ref
37 from xorn.geda.plainformat import *
38 
39 ## Describes the properties of a gEDA schematic/symbol file format version.
40 #
41 # The file format version of a gEDA file is described by a line of the form
42 #
43 # \verbatim
44 # v release_version fileformat_version
45 # \endverbatim
46 #
47 # where \a release_version is an eight-digit number representing a
48 # date and \c fileformat_version is a low integer (usually \c 1 or \c
49 # 2). These represent the file format features that are used:
50 #
51 # Feature | Release version | File format version |
52 # -------------------------------------------|------------------------|------|
53 # Text alignment attribute | >= February 20th, 2000 | |
54 # Line and fill attributes | > September 4th, 2000 | |
55 # Bus pins, whichend and ripperdir attribute | > August 25th, 2002 | |
56 # Text objects with multiple lines | (October 2003) | >= 1 |
57 # Path objects | (November 2008) | >= 2 |
58 #
59 # Depending on the version of the file format, the file is parsed
60 # differently. The unspecified parameters in the older file formats
61 # are set to default values.
62 #
63 # In older libgeda file format versions there was no information about
64 # the active end of pins.
65 
66 class FileFormat:
67  ## Create a new instance from a version number pair and derive its
68  ## properties.
69  def __init__(self, release_ver, fileformat_ver):
70  ## libgeda release version number
71  self.release_ver = release_ver
72  ## libgeda file format version number
73  self.fileformat_ver = fileformat_ver
74 
75  ## Is the text alignment attribute supported?
76  self.supports_text_alignment = release_ver >= VERSION_20000220
77  # yes, above greater or equal (not just greater) is correct.
78  # The format change occurred in 20000220
79 
80  ## Are line and fill attributes supported?
81  self.supports_linefill_attributes = release_ver > VERSION_20000704
82 
83  ## Are bus pins, whichend and ripperdir attributes supported?
84  self.enhanced_pinbus_format = release_ver > VERSION_20020825
85 
86  ## Can text objects have multiple lines?
87  self.supports_multiline_text = fileformat_ver >= 1
88 
89 ## Helper function for \ref sscanf.
90 
91 def parse_token(s, fmt):
92  if fmt == '%c':
93  if len(s) != 1:
94  raise ValueError
95  return s
96 
97  if fmt == '%d':
98  return int(s)
99 
100  if fmt == '%u':
101  val = int(s)
102  if val < 0:
103  raise ValueError
104  return val
105 
106  if fmt == '%s':
107  return s
108 
109  raise ValueError, "Invalid format token: '%s'" % fmt
110 
111 ## Parse a string of space-separated values.
112 #
113 # This is a mock-up version of the standard \c sscanf(3). The format
114 # string must consist of zero or more tokens separated by a space,
115 # optionally followed by a newline character. The format string must
116 # exactly match this pattern. Only the tokens \c %%c, \c %%d, \c %%s,
117 # and \c %%u are allowed.
118 #
119 # \throw ValueError if the string does not match the format
120 # \throw ValueError if an invalid format token is passed
121 #
122 # \return a tuple containing the parsed values
123 
124 def sscanf(s, fmt):
125  while fmt.endswith('\n'):
126  if not s.endswith('\n'):
127  raise ValueError
128  fmt = fmt[:-1]
129  s = s[:-1]
130 
131  if s.endswith('\n'):
132  raise ValueError
133 
134  # gEDA/gaf ignores trailing spaces and, in some older versions,
135  # wrote them for text objects
136  s = s.rstrip(' ')
137 
138  stok = s.split(' ')
139  fmttok = fmt.split(' ')
140 
141  if len(stok) != len(fmttok):
142  raise ValueError
143 
144  return [parse_token(st, ft) for (st, ft) in zip(stok, fmttok)]
145 
146 ## Replace "\r\n" line endings with "\n" line endings.
147 
149  for line in f:
150  if line.endswith('\r\n'):
151  yield line[:-2] + '\n'
152  else:
153  yield line
154 
155 ## Read a symbol or schematic file in libgeda format from a file object.
156 #
157 # \param [in] f A file-like object from which to read
158 # \param [in] name The file name displayed in warning and
159 # error messages
160 # \param [in] log An object to which errors are logged
161 # \param [in] load_symbol Function for loading referenced symbol files
162 # \param [in] load_pixmap Function for loading referenced pixmap files
163 # \param [in] force_boundingbox <i>currently unused</i>
164 #
165 # \returns a transient xorn.proxy.RevisionProxy instance containing
166 # the file's contents
167 #
168 # \throws xorn.geda.read.ParseError if the file is not a valid gEDA
169 # schematic/symbol file
170 
171 def read_file(f, name, log, load_symbol, load_pixmap,
172  force_boundingbox = False):
173  f = codecs.iterdecode(f, 'utf-8')
174  f = strip_carriage_return(f)
175 
176  def lineno_incrementor(f):
177  for line in f:
178  yield line
179  log.lineno += 1
180  f = lineno_incrementor(f)
181 
182  # "Stack" of outer contexts for embedded components
183  object_lists_save = []
184 
185  # Last read object. Attributes and embedded components attach to this.
186  ob = None
187 
188  # This is where read objects end up. Will be swapped for embedded comps.
189  rev = xorn.storage.Revision()
190 
191  # origin for embedded components
192  origin = 0, 0
193 
194  format = FileFormat(0, 0) # no file format definition at all
195 
196  for line in f:
197  if not line:
198  continue
199  objtype = line[0]
200 
201  if objtype == OBJ_LINE:
202  data = read_line(line, origin, format, log)
203  if data is not None:
204  ob = rev.add_object(data)
205  elif objtype == OBJ_NET:
206  data = read_net(line, origin, format, log)
207  if data is not None:
208  ob = rev.add_object(data)
209  elif objtype == OBJ_BUS:
210  data = read_bus(line, origin, format, log)
211  if data is not None:
212  ob = rev.add_object(data)
213  elif objtype == OBJ_BOX:
214  data = read_box(line, origin, format, log)
215  if data is not None:
216  ob = rev.add_object(data)
217  elif objtype == OBJ_PICTURE:
218  data = read_picture(line, f, origin, format, log, load_pixmap)
219  if data is not None:
220  ob = rev.add_object(data)
221  elif objtype == OBJ_CIRCLE:
222  data = read_circle(line, origin, format, log)
223  if data is not None:
224  ob = rev.add_object(data)
225  elif objtype == OBJ_COMPLEX:
226  data = read_complex(line, origin, format, log, load_symbol)
227  if data is not None:
228  ob = rev.add_object(data)
229  elif objtype == OBJ_TEXT:
230  data = read_text(line, f, origin, format, log)
231  if data is not None:
232  ob = rev.add_object(data)
233  elif objtype == OBJ_PATH:
234  data = read_path(line, f, origin, format, log)
235  if data is not None:
236  ob = rev.add_object(data)
237  elif objtype == OBJ_PIN:
238  data = read_pin(line, origin, format, log)
239  if data is not None:
240  ob = rev.add_object(data)
241  elif objtype == OBJ_ARC:
242  data = read_arc(line, origin, format, log)
243  if data is not None:
244  ob = rev.add_object(data)
245  elif objtype == STARTATTACH_ATTR:
246  if ob is None:
247  log.error(_("unexpected attribute list start marker"))
248  continue
249  if not isinstance(rev.get_object_data(ob), xorn.storage.Net) and \
250  not isinstance(rev.get_object_data(ob), xorn.storage.Component):
251  log.error(_("can't attach attributes to this object type"))
252  continue
253 
254  while True:
255  try:
256  line = f.next()
257  except StopIteration:
258  log.error(_("unterminated attribute list"))
259  break
260 
261  if not line:
262  continue
263 
264  if line[0] == ENDATTACH_ATTR:
265  break
266 
267  if line[0] != OBJ_TEXT:
268  log.error(
269  _("tried to attach a non-text item as an attribute"))
270  continue
271 
272  attrib = read_text(line, f, origin, format, log)
273  if attrib is not None:
274  rev.relocate_object(rev.add_object(attrib), ob, None)
275 
276  ob = None
277  elif objtype == START_EMBEDDED:
278  if ob is None:
279  log.error(_("unexpected embedded symbol start marker"))
280  continue
281  component_data = rev.get_object_data(ob)
282  if type(component_data) != xorn.storage.Component:
283  log.error(_("embedded symbol start marker following "
284  "non-component object"))
285  continue
286  if not component_data.symbol.embedded:
287  log.error(_("embedded symbol start marker following "
288  "component with non-embedded symbol"))
289  continue
290  if component_data.symbol.prim_objs is not None:
291  log.error(_("embedded symbol start marker following "
292  "embedded symbol"))
293  continue
294  object_lists_save.append((rev, ob, origin))
295  rev = xorn.storage.Revision()
296  component_data.symbol.prim_objs = rev
297  origin = origin[0] + component_data.x, origin[1] + component_data.y
298  elif objtype == END_EMBEDDED:
299  if not object_lists_save:
300  log.error(_("unexpected embedded symbol end marker"))
301  continue
302  rev, ob, origin = object_lists_save.pop()
303  elif objtype == ENDATTACH_ATTR:
304  log.error(_("unexpected attribute list end marker"))
305  elif objtype == INFO_FONT:
306  # NOP
307  pass
308  elif objtype == COMMENT:
309  # do nothing
310  pass
311  elif objtype == VERSION_CHAR:
312  try:
313  objtype, release_ver, fileformat_ver = \
314  sscanf(line, "%c %u %u\n")
315  except ValueError:
316  try:
317  objtype, release_ver = sscanf(line, "%c %u\n")
318  except ValueError:
319  log.error(_("failed to parse version string"))
320  continue
321  fileformat_ver = 0
322 
323  assert objtype == VERSION_CHAR
324 
325  # 20030921 was the last version which did not have a fileformat
326  # version.
327  if release_ver <= VERSION_20030921:
328  fileformat_ver = 0
329 
330  if fileformat_ver == 0:
331  log.warn(_("Read an old format sym/sch file! "
332  "Please run g[sym|sch]update on this file"))
333 
334  format = FileFormat(release_ver, fileformat_ver)
335  else:
336  log.error(_("read garbage"))
337 
338  for ob in rev.get_objects():
339  data = rev.get_object_data(ob)
340  if not isinstance(data, xorn.storage.Component) \
341  or not data.symbol.embedded:
342  continue
343  if data.symbol.prim_objs is None:
344  log.error(_("embedded symbol is missing"))
345  continue
346 
347  # un-hide overwritten attributes in embedded symbol
348  ob = xorn.proxy.ObjectProxy(rev, ob)
349  visibility = {}
350  for attached in xorn.geda.attrib.find_attached_attribs(ob):
351  attached_name, attached_value = \
352  xorn.geda.attrib.parse_string(attached.text)
353  visibility[attached_name] = attached.visibility
354  for inherited in xorn.geda.attrib.find_inherited_attribs(ob):
355  inherited_name, inherited_value = \
356  xorn.geda.attrib.parse_string(inherited.text)
357  if inherited_name in visibility:
358  inherited.visibility = visibility[inherited_name]
359 
360  if not format.enhanced_pinbus_format:
361  pin_update_whichend(rev, force_boundingbox, log)
362 
363  return xorn.proxy.RevisionProxy(rev)
364 
365 ## Guess the orientation of pins.
366 #
367 # Calculates the bounding box of all pins in the revision. The end of
368 # a pin that is closer to the boundary of the box is set as the active
369 # end.
370 #
371 # \return \c None.
372 #
373 # \warning This function is not implemented. See Xorn bug #148.
374 
375 def pin_update_whichend(rev, force_boundingbox, log):
376  log.error(_("file is lacking pin orientation information"))
377 
378 
379 ## Read a circle object from a string in gEDA format.
380 #
381 # \throw xorn.geda.read.ParseError if the string could not be parsed
382 # \throw ValueError if \a buf doesn't describe a circle object
383 
384 def read_circle(buf, (origin_x, origin_y), format, log):
385  try:
386  if not format.supports_linefill_attributes:
387  type, x1, y1, radius, color = sscanf(buf, "%c %d %d %d %d\n")
388  circle_width = 0
389  circle_end = 0
390  circle_type = 0
391  circle_length = -1
392  circle_space = -1
393 
394  circle_fill = 0
395  fill_width = 0
396  angle1 = -1
397  pitch1 = -1
398  angle2 = -1
399  pitch2 = -1
400  else:
401  type, x1, y1, radius, color, circle_width, \
402  circle_end, circle_type, circle_length, circle_space, \
403  circle_fill, fill_width, angle1, pitch1, angle2, pitch2 = sscanf(
404  buf, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n")
405  except ValueError:
406  log.error(_("failed to parse circle object"))
407  return None
408 
409  if type != OBJ_CIRCLE:
410  raise ValueError
411 
412  if radius == 0:
413  log.warn(_("circle has radius zero"))
414  elif radius < 0:
415  log.warn(_("circle has negative radius (%d), setting to 0") % radius)
416  radius = 0
417 
418  if color < 0 or color > MAX_COLORS:
419  log.warn(_("circle has invalid color (%d), setting to %d")
420  % (color, DEFAULT_COLOR))
421  color = DEFAULT_COLOR
422 
423  return xorn.storage.Circle(
424  x = x1 - origin_x,
425  y = y1 - origin_y,
426  radius = radius,
427  color = color,
428  line = xorn.storage.LineAttr(
429  width = circle_width,
430  cap_style = circle_end,
431  dash_style = circle_type,
432  dash_length = circle_length,
433  dash_space = circle_space),
434  fill = xorn.storage.FillAttr(
435  type = circle_fill,
436  width = fill_width,
437  angle0 = angle1,
438  pitch0 = pitch1,
439  angle1 = angle2,
440  pitch1 = pitch2))
441 
442 ## Read an arc object from a string in gEDA format.
443 #
444 # A negative or null radius is not allowed.
445 #
446 # \throw xorn.geda.read.ParseError if the string could not be parsed
447 # \throw ValueError if \a buf doesn't describe a arc object
448 
449 def read_arc(buf, (origin_x, origin_y), format, log):
450  try:
451  if not format.supports_linefill_attributes:
452  type, x1, y1, radius, start_angle, sweep_angle, color = sscanf(
453  buf, "%c %d %d %d %d %d %d\n")
454  arc_width = 0
455  arc_end = 0
456  arc_type = 0
457  arc_space = -1
458  arc_length = -1
459  else:
460  type, x1, y1, radius, start_angle, sweep_angle, color, \
461  arc_width, arc_end, arc_type, arc_length, arc_space = sscanf(
462  buf, "%c %d %d %d %d %d %d %d %d %d %d %d\n")
463  except ValueError:
464  log.error(_("failed to parse arc object"))
465  return None
466 
467  if type != OBJ_ARC:
468  raise ValueError
469 
470  if radius == 0:
471  log.warn(_("arc has radius zero"))
472  elif radius < 0:
473  log.warn(_("arc has negative radius (%d), setting to 0") % radius)
474  radius = 0
475 
476  if color < 0 or color > MAX_COLORS:
477  log.warn(_("arc has invalid color (%d), setting to %d")
478  % (color, DEFAULT_COLOR))
479  color = DEFAULT_COLOR
480 
481  return xorn.storage.Arc(
482  x = x1 - origin_x,
483  y = y1 - origin_y,
484  radius = radius,
485  startangle = start_angle,
486  sweepangle = sweep_angle,
487  color = color,
488  line = xorn.storage.LineAttr(
489  width = arc_width,
490  cap_style = arc_end,
491  dash_style = arc_type,
492  dash_length = arc_length,
493  dash_space = arc_space))
494 
495 ## Read a box object from a string in gEDA format.
496 #
497 # \throw xorn.geda.read.ParseError if the string could not be parsed
498 # \throw ValueError if \a buf doesn't describe a box object
499 
500 def read_box(buf, (origin_x, origin_y), format, log):
501  try:
502  if not format.supports_linefill_attributes:
503  type, x1, y1, width, height, color = sscanf(
504  buf, "%c %d %d %d %d %d\n")
505  box_width = 0
506  box_end = 0
507  box_type = 0
508  box_length = -1
509  box_space = -1
510 
511  box_filling = 0
512  fill_width = 0
513  angle1 = -1
514  pitch1 = -1
515  angle2 = -1
516  pitch2 = -1
517  else:
518  type, x1, y1, width, height, color, \
519  box_width, box_end, box_type, box_length, box_space, \
520  box_filling, fill_width, angle1, pitch1, angle2, pitch2 = sscanf(
521  buf, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n")
522  except ValueError:
523  log.error(_("failed to parse box object"))
524  return None
525 
526  if type != OBJ_BOX:
527  raise ValueError
528 
529  if width == 0 or height == 0:
530  log.warn(_("box has width/height zero"))
531 
532  if color < 0 or color > MAX_COLORS:
533  log.warn(_("box has invalid color (%d), setting to %d")
534  % (color, DEFAULT_COLOR))
535  color = DEFAULT_COLOR
536 
537  # In libgeda, a box is internally represented by its lower right
538  # and upper left corner, whereas in the file format, it is
539  # described as its lower left corner and its width and height.
540  #
541  # We don't care and just use the file format representation.
542 
543  return xorn.storage.Box(
544  x = x1 - origin_x,
545  y = y1 - origin_y,
546  width = width,
547  height = height,
548  color = color,
549  line = xorn.storage.LineAttr(
550  width = box_width,
551  cap_style = box_end,
552  dash_style = box_type,
553  dash_length = box_length,
554  dash_space = box_space),
555  fill = xorn.storage.FillAttr(
556  type = box_filling,
557  width = fill_width,
558  angle0 = angle1,
559  pitch0 = pitch1,
560  angle1 = angle2,
561  pitch1 = pitch2))
562 
563 ## Read a bus object from a string in gEDA format.
564 #
565 # \throw xorn.geda.read.ParseError if the string could not be parsed
566 # \throw ValueError if \a buf doesn't describe a bus object
567 
568 def read_bus(buf, (origin_x, origin_y), format, log):
569  try:
570  if not format.enhanced_pinbus_format:
571  type, x1, y1, x2, y2, color = sscanf(
572  buf, "%c %d %d %d %d %d\n")
573  ripper_dir = 0
574  else:
575  type, x1, y1, x2, y2, color, ripper_dir = sscanf(
576  buf, "%c %d %d %d %d %d %d\n")
577  except ValueError:
578  log.error(_("failed to parse bus object"))
579  return None
580 
581  if type != OBJ_BUS:
582  raise ValueError
583 
584  if x1 == x2 and y1 == y2:
585  log.warn(_("bus has length zero"))
586 
587  if color < 0 or color > MAX_COLORS:
588  log.warn(_("bus has invalid color (%d), setting to %d")
589  % (color, DEFAULT_COLOR))
590  color = DEFAULT_COLOR
591 
592  if ripper_dir < -1 or ripper_dir > 1:
593  log.warn(_("bus has invalid ripper direction (%d)") % ripper_dir)
594  ripper_dir = 0 # isn't used
595 
596  return xorn.storage.Net(
597  x = x1 - origin_x,
598  y = y1 - origin_y,
599  width = x2 - x1,
600  height = y2 - y1,
601  color = color,
602  is_bus = True,
603  is_pin = False,
604  is_inverted = False)
605 
606 ## Read a component object from a string in gEDA format.
607 #
608 # If the symbol is not embedded, use the function \a load_symbol to
609 # load it from the component library.
610 #
611 # \throw xorn.geda.read.ParseError if the string could not be parsed
612 # \throw ValueError if \a buf doesn't describe a
613 # component object
614 
615 def read_complex(buf, (origin_x, origin_y), format, log, load_symbol):
616  try:
617  type, x1, y1, selectable, angle, mirror, basename = sscanf(
618  buf, "%c %d %d %d %d %d %s\n")
619  except ValueError:
620  log.error(_("failed to parse complex object"))
621  return None
622 
623  if type != OBJ_COMPLEX:
624  raise ValueError
625 
626  if angle not in [0, 90, 180, 270]:
627  log.warn(_("component has invalid angle (%d), setting to 0") % angle)
628  angle = 0
629 
630  if mirror != 0 and mirror != 1:
631  log.warn(_("component has invalid mirror flag (%d), "
632  "setting to 0") % mirror)
633  mirror = 0
634 
635  # color = DEFAULT_COLOR
636 
637  if basename.startswith('EMBEDDED'):
638  symbol = xorn.geda.ref.Symbol(basename[8:], None, True)
639  else:
640  symbol = load_symbol(basename, False)
641  assert not symbol.embedded
642 
643  return xorn.storage.Component(
644  x = x1 - origin_x,
645  y = y1 - origin_y,
646  selectable = selectable,
647  angle = angle,
648  mirror = mirror,
649  symbol = symbol)
650 
651 ## Read a line object from a string in gEDA format.
652 #
653 # \throw xorn.geda.read.ParseError if the string could not be parsed
654 # \throw ValueError if \a buf doesn't describe a line object
655 
656 def read_line(buf, (origin_x, origin_y), format, log):
657  try:
658  if not format.supports_linefill_attributes:
659  type, x1, y1, x2, y2, color = sscanf(buf, "%c %d %d %d %d %d\n")
660  line_width = 0
661  line_end = 0
662  line_type = 0
663  line_length = -1
664  line_space = -1
665  else:
666  type, x1, y1, x2, y2, color, \
667  line_width, line_end, line_type, line_length, line_space = sscanf(
668  buf, "%c %d %d %d %d %d %d %d %d %d %d\n")
669  except ValueError:
670  log.error(_("failed to parse line object"))
671  return None
672 
673  if type != OBJ_LINE:
674  raise ValueError
675 
676  # Null length line are not allowed. If such a line is detected, a
677  # message is issued.
678  if x1 == x2 and y1 == y2:
679  log.warn(_("line has length zero"))
680 
681  if color < 0 or color > MAX_COLORS:
682  log.warn(_("line has invalid color (%d), setting to %d")
683  % (color, DEFAULT_COLOR))
684  color = DEFAULT_COLOR
685 
686  return xorn.storage.Line(
687  x = x1 - origin_x,
688  y = y1 - origin_y,
689  width = x2 - x1,
690  height = y2 - y1,
691  color = color,
692  line = xorn.storage.LineAttr(
693  width = line_width,
694  cap_style = line_end,
695  dash_style = line_type,
696  dash_length = line_length,
697  dash_space = line_space))
698 
699 ## Read a net object from a string in gEDA format.
700 #
701 # \throw xorn.geda.read.ParseError if the string could not be parsed
702 # \throw ValueError if \a buf doesn't describe a net object
703 
704 def read_net(buf, (origin_x, origin_y), format, log):
705  try:
706  type, x1, y1, x2, y2, color = sscanf(buf, "%c %d %d %d %d %d\n")
707  except ValueError:
708  log.error(_("failed to parse net object"))
709  return None
710 
711  if type != OBJ_NET:
712  raise ValueError
713 
714  if x1 == x2 and y1 == y2:
715  log.warn(_("net has length zero"))
716 
717  if color < 0 or color > MAX_COLORS:
718  log.warn(_("net has invalid color (%d), setting to %d")
719  % (color, DEFAULT_COLOR))
720  color = DEFAULT_COLOR
721 
722  return xorn.storage.Net(
723  x = x1 - origin_x,
724  y = y1 - origin_y,
725  width = x2 - x1,
726  height = y2 - y1,
727  color = color,
728  is_bus = False,
729  is_pin = False,
730  is_inverted = False)
731 
732 ## Read a path object from a string and a file in gEDA format.
733 #
734 # Creates a path object from the string \a first_line and reads as
735 # many lines describing the path as specified there from \a f.
736 #
737 # \throw xorn.geda.read.ParseError if the string could not be parsed
738 # \throw xorn.geda.read.ParseError if not enough lines could be read
739 # from the file
740 # \throw ValueError if \a first_line doesn't describe a
741 # path object
742 
743 def read_path(first_line, f, (origin_x, origin_y), format, log):
744  try:
745  type, color, \
746  line_width, line_end, line_type, line_length, line_space, \
747  fill_type, fill_width, angle1, pitch1, angle2, pitch2, \
748  num_lines = sscanf(
749  first_line, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n")
750  except ValueError:
751  log.error(_("failed to parse path object"))
752  return None
753 
754  if type != OBJ_PATH:
755  raise ValueError
756 
757  # Checks if the required color is valid.
758  if color < 0 or color > MAX_COLORS:
759  log.warn(_("path has invalid color (%d), setting to %d")
760  % (color, DEFAULT_COLOR))
761  color = DEFAULT_COLOR
762 
763  pathstr = ''
764  for i in xrange(0, num_lines):
765  try:
766  line = f.next()
767  except StopIteration:
768  log.error(_("unexpected end of file after %d lines "
769  "while reading path") % i)
770  break
771 
772  pathstr += line
773 
774  if pathstr.endswith('\n'):
775  pathstr = pathstr[:-1]
776 
777  return xorn.storage.Path(
778  pathdata = pathstr.encode('utf-8'),
779  color = color,
780  line = xorn.storage.LineAttr(
781  width = line_width,
782  cap_style = line_end,
783  dash_style = line_type,
784  dash_length = line_length,
785  dash_space = line_space),
786  fill = xorn.storage.FillAttr(
787  type = fill_type,
788  width = fill_width,
789  angle0 = angle1,
790  pitch0 = pitch1,
791  angle1 = angle2,
792  pitch1 = pitch2))
793 
794 ## Read a picture object from a string and a file in gEDA format.
795 #
796 # Creates a picture object from the string \a first_line. If the
797 # pixmap is not embedded, uses the function \a load_pixmap to load it
798 # from an external file. If the pixmap is embedded, reads its data in
799 # base64 encoding from \a f.
800 #
801 # \throw xorn.geda.read.ParseError if the string could not be parsed
802 # \throw xorn.geda.read.ParseError if the picture data could be read
803 # from the file
804 # \throw ValueError if \a first_line doesn't describe a
805 # picture object
806 
807 def read_picture(first_line, f, (origin_x, origin_y), format, log,
808  load_pixmap):
809  try:
810  type, x1, y1, width, height, angle, mirrored, embedded = sscanf(
811  first_line, "%c %d %d %d %d %d %d %d\n")
812  except ValueError:
813  log.error(_("failed to parse picture definition"))
814  return None
815 
816  if type != OBJ_PICTURE:
817  raise ValueError
818 
819  if width == 0 or height == 0:
820  log.warn(_("picture has width/height zero"))
821 
822  if mirrored != 0 and mirrored != 1:
823  log.warn(_("picture has wrong 'mirrored' parameter (%d), "
824  "setting to 0") % mirrored)
825  mirrored = 0
826 
827  if angle not in [0, 90, 180, 270]:
828  log.warn(_("picture has unsupported angle (%d), setting to 0") % angle)
829  angle = 0
830 
831  try:
832  filename = f.next()
833  except StopIteration:
834  log.error(_("unexpected end of file while reading picture file name"))
835  filename = ''
836  else:
837  if filename.endswith('\n'):
838  filename = filename[:-1]
839 
840  # Handle empty filenames
841  if not filename:
842  log.warn(_("image has no filename"))
843  filename = None
844 
845  if embedded != 1:
846  if embedded != 0:
847  log.warn(_("picture has wrong 'embedded' parameter (%d), "
848  "setting to not embedded") % embedded)
849  pixmap = load_pixmap(filename, False)
850  assert not pixmap.embedded
851  else:
852  pixmap = xorn.geda.ref.Pixmap(filename, None, True)
853  # Read the encoded picture
854  try:
855  pixmap.data = xorn.base64.decode(f, delim = '.')
856  except xorn.base64.DecodingError as e:
857  log.error(_("failed to load image from embedded data: %s")
858  % e.message)
859  pixmap.data = ''
860 
861  return xorn.storage.Picture(
862  x = x1 - origin_x,
863  y = y1 - origin_y,
864  width = width,
865  height = height,
866  angle = angle,
867  mirror = mirrored,
868  pixmap = pixmap)
869 
870 ## Read a pin object from a string in gEDA format.
871 #
872 # \throw xorn.geda.read.ParseError if the string could not be parsed
873 # \throw ValueError if \a buf doesn't describe a pin object
874 
875 def read_pin(buf, (origin_x, origin_y), format, log):
876  try:
877  if not format.enhanced_pinbus_format:
878  type, x1, y1, x2, y2, color = sscanf(buf, "%c %d %d %d %d %d\n")
879  pin_type = 0
880  whichend = -1
881  else:
882  type, x1, y1, x2, y2, color, pin_type, whichend = sscanf(
883  buf, "%c %d %d %d %d %d %d %d\n")
884  except ValueError:
885  log.error(_("failed to parse pin object"))
886  return None
887 
888  if type != OBJ_PIN:
889  raise ValueError
890 
891  if whichend == -1:
892  log.warn(_("pin does not have the whichone field set--"
893  "verify and correct manually!"))
894  elif whichend < -1 or whichend > 1:
895  log.warn(_("pin has invalid whichend (%d), "
896  "setting to first end") % whichend)
897  whichend = 0
898 
899  if color < 0 or color > MAX_COLORS:
900  log.warn(_("pin has invalid color (%d), setting to %d")
901  % (color, DEFAULT_COLOR))
902  color = DEFAULT_COLOR
903 
904  if pin_type == 0:
905  is_bus = False
906  elif pin_type == 1:
907  is_bus = True
908  else:
909  log.warn(_("pin has invalid type (%d), setting to 0") % pin_type)
910  is_bus = False
911 
912  if whichend != 1:
913  is_inverted = False
914  else:
915  x1, y1, x2, y2 = x2, y2, x1, y1
916  is_inverted = True
917 
918  return xorn.storage.Net(
919  x = x1 - origin_x,
920  y = y1 - origin_y,
921  width = x2 - x1,
922  height = y2 - y1,
923  color = color,
924  is_bus = is_bus,
925  is_pin = True,
926  is_inverted = is_inverted)
927 
928 ## Read a text object from a string and a file in gEDA format.
929 #
930 # Creates a text object from the string \a first_line and reads as
931 # many lines of text as specified there from \a f.
932 #
933 # \throw xorn.geda.read.ParseError if the string could not be parsed
934 # \throw xorn.geda.read.ParseError if not enough lines could be read
935 # from the file
936 # \throw ValueError if \a first_line doesn't describe a
937 # text object
938 
939 def read_text(first_line, f, (origin_x, origin_y), format, log):
940  try:
941  if format.supports_multiline_text:
942  type, x, y, color, size, visibility, show_name_value, angle, \
943  alignment, num_lines = sscanf(
944  first_line, "%c %d %d %d %d %d %d %d %d %d\n")
945  elif not format.supports_text_alignment:
946  type, x, y, color, size, visibility, show_name_value, angle = \
947  sscanf(first_line, "%c %d %d %d %d %d %d %d\n")
948  alignment = LOWER_LEFT # older versions didn't have this
949  num_lines = 1 # only support a single line
950  else:
951  type, x, y, color, size, visibility, show_name_value, angle, \
952  alignment = sscanf(
953  first_line, "%c %d %d %d %d %d %d %d %d\n")
954  num_lines = 1 # only support a single line
955  except ValueError:
956  log.error(_("failed to parse text object"))
957  return None
958 
959  if type != OBJ_TEXT:
960  raise ValueError
961 
962  if size == 0:
963  log.warn(_("text has size zero"))
964 
965  if angle not in [0, 90, 180, 270]:
966  log.warn(_("text has unsupported angle (%d), setting to 0") % angle)
967  angle = 0
968 
969  if alignment not in [LOWER_LEFT, MIDDLE_LEFT, UPPER_LEFT,
970  LOWER_MIDDLE, MIDDLE_MIDDLE, UPPER_MIDDLE,
971  LOWER_RIGHT, MIDDLE_RIGHT, UPPER_RIGHT]:
972  log.warn(_("text has unsupported alignment (%d), "
973  "setting to LOWER_LEFT") % alignment)
974  alignment = LOWER_LEFT
975 
976  if color < 0 or color > MAX_COLORS:
977  log.warn(_("text has invalid color (%d), setting to %d")
978  % (color, DEFAULT_COLOR))
979  color = DEFAULT_COLOR
980 
981  if num_lines <= 0:
982  log.error(_("text has invalid number of lines (%d)") % num_lines)
983 
984  text = ''
985  for i in xrange(0, num_lines):
986  try:
987  line = f.next()
988  except StopIteration:
989  log.error(_("unexpected end of file after %d lines of text") % i)
990  break
991 
992  text += line
993 
994  if text.endswith('\n'):
995  text = text[:-1]
996 
997  tmp = text.replace('\\\\', '')
998  if tmp.count('\\_') % 2:
999  log.warn(_("mismatched overbar markers"))
1000  if '\\' in tmp.replace('\\_', ''):
1001  log.warn(_("stray backslash character(s)"))
1002 
1003  return xorn.storage.Text(
1004  x = x - origin_x,
1005  y = y - origin_y,
1006  color = color,
1007  text_size = size,
1008  visibility = visibility,
1009  show_name_value = show_name_value,
1010  angle = angle,
1011  alignment = alignment,
1012  text = text.encode('utf-8'))
def read_line
Read a line object from a string in gEDA format.
Definition: plainread.py:656
def read_complex
Read a component object from a string in gEDA format.
Definition: plainread.py:615
fileformat_ver
libgeda file format version number
Definition: plainread.py:73
Reading and writing base64-encoded data.
Definition: base64.py:1
Attribute parsing and lookup.
Definition: attrib.py:1
Reading schematic/symbol files.
Definition: read.py:1
Schematic net segment, bus segment, or pin.
Definition: storage.py:549
Schematic arc.
Definition: storage.py:492
def read_arc
Read an arc object from a string in gEDA format.
Definition: plainread.py:449
supports_multiline_text
Can text objects have multiple lines?
Definition: plainread.py:87
Raised when reading invalid or unterminated base64-encoded data.
Definition: base64.py:119
def find_attached_attribs
Return the attributes directly attached to a net or component.
Definition: attrib.py:103
High-level revision proxy class.
Definition: proxy.py:24
def __init__
Create a new instance from a version number pair and derive its properties.
Definition: plainread.py:69
def read_path
Read a path object from a string and a file in gEDA format.
Definition: plainread.py:743
Schematic picture.
Definition: storage.py:571
def find_inherited_attribs
Return the attributes inherited by a component via its symbol.
Definition: attrib.py:111
gEDA schematic/symbol file format constants
Definition: plainformat.py:1
Schematic path.
Definition: storage.py:562
def parse_token
Helper function for sscanf.
Definition: plainread.py:91
Schematic box.
Definition: storage.py:504
Schematic component.
Definition: storage.py:527
Schematic circle.
Definition: storage.py:516
def read_net
Read a net object from a string in gEDA format.
Definition: plainread.py:704
Schematic fill style.
Definition: storage.py:481
def decode
Read a string in base64 representation from a file.
Definition: base64.py:138
High-level object proxy class.
Definition: proxy.py:77
def pin_update_whichend
Guess the orientation of pins.
Definition: plainread.py:375
def read_box
Read a box object from a string in gEDA format.
Definition: plainread.py:500
Schematic line style.
Definition: storage.py:471
def read_text
Read a text object from a string and a file in gEDA format.
Definition: plainread.py:939
def read_circle
Read a circle object from a string in gEDA format.
Definition: plainread.py:384
Schematic text or attribute.
Definition: storage.py:583
enhanced_pinbus_format
Are bus pins, whichend and ripperdir attributes supported?
Definition: plainread.py:84
High-level proxy classes for the storage backend.
Definition: proxy.py:1
A particular state of the contents of a file.
Definition: storage.py:35
def read_bus
Read a bus object from a string in gEDA format.
Definition: plainread.py:568
Referenced symbols and pixmaps.
Definition: ref.py:1
supports_text_alignment
Is the text alignment attribute supported?
Definition: plainread.py:76
Describes the properties of a gEDA schematic/symbol file format version.
Definition: plainread.py:66
def read_pin
Read a pin object from a string in gEDA format.
Definition: plainread.py:875
Xorn storage backend.
Definition: storage.py:1
def strip_carriage_return
Replace "\r\n" line endings with "\n" line endings.
Definition: plainread.py:148
def read_file
Read a symbol or schematic file in libgeda format from a file object.
Definition: plainread.py:172
Schematic line.
Definition: storage.py:538
supports_linefill_attributes
Are line and fill attributes supported?
Definition: plainread.py:81
def sscanf
Parse a string of space-separated values.
Definition: plainread.py:124
def parse_string
Parse an attribute string of the form name=value into its name and value parts.
Definition: attrib.py:55
def read_picture
Read a picture object from a string and a file in gEDA format.
Definition: plainread.py:808
release_ver
libgeda release version number
Definition: plainread.py:71