[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

gEDA-cvs: gaf.git: branch: master updated (1.5.0-20080706-184-g5093b71)



The branch, master has been updated
       via  5093b71054669254951550f532ee1c6a52ae3ab9 (commit)
       via  1fffc27a3b62cc32a70edcc1ee89ff458318c6bc (commit)
       via  e9f4a9a4bb05bc554e165b2215f8e7358b2315de (commit)
       via  699f46d8a598c1cfa972f0ddf80e39d18c907ece (commit)
      from  dc402ee9c0b5196634c83ed8fd2e2f059a4338cb (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


=========
 Summary
=========

 gschem/include/gschem_struct.h   |    4 +
 gschem/include/prototype.h       |   15 +
 gschem/po/POTFILES.in            |    1 +
 gschem/src/Makefile.am           |    1 +
 gschem/src/gschem.c              |    1 +
 gschem/src/gschem_toplevel.c     |    2 +
 gschem/src/i_callbacks.c         |    6 +-
 gschem/src/o_basic.c             |    2 +
 gschem/src/o_grips.c             |  291 +++++++++++++---
 gschem/src/o_path.c              |  550 +++++++++++++++++++++++++++++
 libgeda/include/funcs.h          |    1 +
 libgeda/include/o_types.h        |    1 +
 libgeda/include/prototype.h      |   12 +
 libgeda/include/prototype_priv.h |    9 +
 libgeda/include/struct.h         |   27 ++
 libgeda/po/POTFILES.in           |    1 +
 libgeda/src/Makefile.am          |    2 +
 libgeda/src/a_basic.c            |   11 +
 libgeda/src/f_print.c            |    7 +-
 libgeda/src/o_attrib.c           |   13 +
 libgeda/src/o_basic.c            |   11 +
 libgeda/src/o_complex_basic.c    |    7 +
 libgeda/src/o_list.c             |    4 +
 libgeda/src/o_path_basic.c       |  707 ++++++++++++++++++++++++++++++++++++++
 libgeda/src/s_basic.c            |    6 +
 libgeda/src/s_path.c             |  698 +++++++++++++++++++++++++++++++++++++
 symbols/analog/npn-1.sym         |   19 +-
 symbols/analog/npn-2.sym         |   19 +-
 symbols/analog/npn-3.sym         |   19 +-
 symbols/analog/npn-IGBT-1.sym    |   19 +-
 symbols/analog/pnp-1.sym         |   19 +-
 symbols/analog/pnp-2.sym         |   19 +-
 symbols/analog/pnp-3.sym         |   19 +-
 33 files changed, 2407 insertions(+), 116 deletions(-)
 create mode 100644 gschem/src/o_path.c
 create mode 100644 libgeda/src/o_path_basic.c
 create mode 100644 libgeda/src/s_path.c


=================
 Commit Messages
=================

commit 5093b71054669254951550f532ee1c6a52ae3ab9
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:58:18 2008 +0100

    symbols: Add solid polygon arrow-heads to transistor emitters
    
    Takes advantage of the new path / polygon support.

:100644 100644 c2d2db5... fcf3914... M	symbols/analog/npn-1.sym
:100644 100644 3f07e5a... 7d5a651... M	symbols/analog/npn-2.sym
:100644 100644 a6b1e7d... a2f1e1a... M	symbols/analog/npn-3.sym
:100644 100644 f81c4a7... e171f11... M	symbols/analog/npn-IGBT-1.sym
:100644 100644 2c7df75... 03d915c... M	symbols/analog/pnp-1.sym
:100644 100644 e8968a2... f37005f... M	symbols/analog/pnp-2.sym
:100644 100644 60c586d... 56f9f7b... M	symbols/analog/pnp-3.sym

commit 1fffc27a3b62cc32a70edcc1ee89ff458318c6bc
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:57:52 2008 +0100

    Add support for arbitrary paths (filled and unfilled)
    
    The data-structures and on-disk format (which utiliseSVG style path
    strings) support stright line and bezier curve segments. Paths can be
    closed or open, and store line style and fill information in common with
    other geda objects.
    
    The path string parser is based on (slightly modified) code from librsvg
    version 2.22.2 (LGPL). Code from rsvg-path.c and rsvg-bpath-util.c
    (Copyright (C) 2000 Eazel, Inc.) was combined to form libgeda/src/s_path.c.
    These have been modified to use gEDA defined path data structures.
    
    The rendering implementation is based upon a small porion of the function
    rsvg_cairo_render_path (..) from rsvg-cairo-draw.c
    
    The parser still retains support the full richness of SVG path strings,
    although all instructions are subsequently normalised on to move, line, bezier
    and "close path" records. This support retained for convenience when entering
    artwork (which can be copied from an SVG editor). When saved, normalised paths
    are written out.
    
    The current rendering and shortest-distance computations are limited to
    straight line segments. Bezier segments, where encountered are treated
    as a straight line between their two endpoints.

:100644 100644 7fe2103... 9fa556c... M	gschem/include/prototype.h
:100644 100644 f59346f... e3f4350... M	gschem/po/POTFILES.in
:100644 100644 4f35308... f9db66c... M	gschem/src/Makefile.am
:100644 100644 80c6934... 633d33d... M	gschem/src/gschem.c
:100644 100644 bcc1935... e5f72b8... M	gschem/src/i_callbacks.c
:100644 100644 212d700... 4246189... M	gschem/src/o_basic.c
:100644 100644 964bf25... 29f8b37... M	gschem/src/o_grips.c
:000000 100644 0000000... 8ca7ffa... A	gschem/src/o_path.c
:100644 100644 11cbb2f... b6b2fbd... M	libgeda/include/funcs.h
:100644 100644 ba0b25b... bd5e4b5... M	libgeda/include/o_types.h
:100644 100644 76cd6aa... ecb19d0... M	libgeda/include/prototype.h
:100644 100644 4f585af... 659f6c4... M	libgeda/include/prototype_priv.h
:100644 100644 4224034... 3636802... M	libgeda/include/struct.h
:100644 100644 5161824... 6e70553... M	libgeda/po/POTFILES.in
:100644 100644 55c80ce... bcae051... M	libgeda/src/Makefile.am
:100644 100644 4d10ef8... 5d50f26... M	libgeda/src/a_basic.c
:100644 100644 97b9635... 61e6924... M	libgeda/src/f_print.c
:100644 100644 b67a770... a967339... M	libgeda/src/o_attrib.c
:100644 100644 28ddd54... 2c9f80a... M	libgeda/src/o_basic.c
:100644 100644 8d02aee... 284c5f1... M	libgeda/src/o_complex_basic.c
:100644 100644 15d5e11... 4eece4c... M	libgeda/src/o_list.c
:000000 100644 0000000... 439d8b2... A	libgeda/src/o_path_basic.c
:100644 100644 8ce1fd5... b498b8f... M	libgeda/src/s_basic.c
:000000 100644 0000000... 36065b0... A	libgeda/src/s_path.c

commit e9f4a9a4bb05bc554e165b2215f8e7358b2315de
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:57:44 2008 +0100

    Move some static variables inside o_grips.c to GSCHEM_TOPLEVEL
    
    This is necessary for grip manipulation of more complex OBJECT types
    such as the forthcoming PATHs. As the entire drawing state can't of a
    path can't conveniently be stashed in the GSCHEM_TOPLEVEL variables
    such as first_w{x,y}, second_w{x,y} etc., we need to reference the
    OBJECT and grip being manipulated from the drawing functions which
    are outside o_grips.c.

:100644 100644 d82fe40... b16b4ea... M	gschem/include/gschem_struct.h
:100644 100644 d3f8b3e... 8c67769... M	gschem/src/gschem_toplevel.c
:100644 100644 c648865... 964bf25... M	gschem/src/o_grips.c

commit 699f46d8a598c1cfa972f0ddf80e39d18c907ece
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:57:37 2008 +0100

    Fix f_print_set_line_width() to emit valid postscript
    
    Previously this function emitted "%d mils setlinewidth\n". The "mils"
    portion seems to be left over from previous printing code, as "mils"
    isn't defined to be anything in prolog.ps, nor in the headers we emit.
    Our units are scaled to mils, so we can just emit "%d setlinewidth\n".

:100644 100644 1820544... 97b9635... M	libgeda/src/f_print.c

=========
 Changes
=========

commit 5093b71054669254951550f532ee1c6a52ae3ab9
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:58:18 2008 +0100

    symbols: Add solid polygon arrow-heads to transistor emitters
    
    Takes advantage of the new path / polygon support.

diff --git a/symbols/analog/npn-1.sym b/symbols/analog/npn-1.sym
index c2d2db5..fcf3914 100644
--- a/symbols/analog/npn-1.sym
+++ b/symbols/analog/npn-1.sym
@@ -1,18 +1,20 @@
-v 20031231 1
+v 20080706 1
 L 200 800 200 200 3 0 0 0 -1 -1
 T 600 500 5 10 0 0 0 0 1
 device=NPN_TRANSISTOR
 L 500 800 200 500 3 0 0 0 -1 -1
 L 200 500 500 200 3 0 0 0 -1 -1
+p 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 1
+M 410,240 L 501,200 L 455,295 L 435,265 z
 P 0 500 200 500 1 0 0
 {
 T 100 550 5 6 1 1 0 0 1
 pinnumber=B
 T 100 550 5 6 0 0 0 0 1
 pinseq=2
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pinlabel=B
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 1000 500 800 1 0 0
@@ -21,9 +23,9 @@ T 400 850 5 6 1 1 0 0 1
 pinnumber=C
 T 400 850 5 6 0 0 0 0 1
 pinseq=1
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pinlabel=C
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 200 500 0 1 0 1
@@ -32,13 +34,10 @@ T 400 50 5 6 1 1 0 0 1
 pinnumber=E
 T 400 50 5 6 0 0 0 0 1
 pinseq=3
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pinlabel=E
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pintype=pas
 }
-L 500 200 450 300 3 0 0 0 -1 -1
-L 500 200 400 250 3 0 0 0 -1 -1
-L 400 250 450 300 3 0 0 0 -1 -1
 T 600 500 8 10 1 1 0 0 1
 refdes=Q?
diff --git a/symbols/analog/npn-2.sym b/symbols/analog/npn-2.sym
index 3f07e5a..7d5a651 100644
--- a/symbols/analog/npn-2.sym
+++ b/symbols/analog/npn-2.sym
@@ -1,4 +1,4 @@
-v 20031231 1
+v 20080706 1
 L 200 800 200 200 3 0 0 0 -1 -1
 T 600 500 5 10 0 0 0 0 1
 device=NPN_TRANSISTOR
@@ -8,9 +8,9 @@ T 100 550 5 6 1 1 0 0 1
 pinnumber=B
 T 100 550 5 6 0 0 0 0 1
 pinseq=2
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pinlabel=B
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 1000 500 800 1 0 0
@@ -19,9 +19,9 @@ T 400 850 5 6 1 1 0 0 1
 pinnumber=C
 T 400 850 5 6 0 0 0 0 1
 pinseq=1
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pinlabel=C
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 200 500 0 1 0 1
@@ -30,15 +30,14 @@ T 400 50 5 6 1 1 0 0 1
 pinnumber=E
 T 400 50 5 6 0 0 0 0 1
 pinseq=3
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pinlabel=E
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pintype=pas
 }
 L 200 650 500 800 3 0 0 0 -1 -1
 L 200 350 500 200 3 0 0 0 -1 -1
-L 501 200 403 209 3 0 0 0 -1 -1
-L 500 200 431 273 3 0 0 0 -1 -1
-L 403 209 431 273 3 0 0 0 -1 -1
 T 600 500 8 10 1 1 0 0 1
 refdes=Q?
+p 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 1
+M 403,209 L 501,200 L 431,273 L 425,240 z
diff --git a/symbols/analog/npn-3.sym b/symbols/analog/npn-3.sym
index a6b1e7d..a2f1e1a 100644
--- a/symbols/analog/npn-3.sym
+++ b/symbols/analog/npn-3.sym
@@ -1,13 +1,13 @@
-v 20031231 1
+v 20080706 1
 P 600 1000 600 800 1 0 0
 {
 T 500 850 5 6 1 1 0 0 1
 pinnumber=C
 T 500 850 5 6 0 0 0 0 1
 pinseq=1
-T 500 850 5 6 0 1 0 0 1 
+T 500 850 5 6 0 1 0 0 1
 pinlabel=C
-T 500 850 5 6 0 1 0 0 1 
+T 500 850 5 6 0 1 0 0 1
 pintype=pas
 }
 P 600 200 600 0 1 0 1
@@ -16,9 +16,9 @@ T 500 50 5 6 1 1 0 0 1
 pinnumber=E
 T 500 50 5 6 0 0 0 0 1
 pinseq=3
-T 500 50 5 6 0 1 0 0 1 
+T 500 50 5 6 0 1 0 0 1
 pinlabel=E
-T 500 50 5 6 0 1 0 0 1 
+T 500 50 5 6 0 1 0 0 1
 pintype=pas
 }
 V 500 501 316 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1
@@ -33,14 +33,13 @@ T 100 550 5 6 1 1 0 0 1
 pinnumber=B
 T 100 550 5 6 0 0 0 0 1
 pinseq=2
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pinlabel=B
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pintype=pas
 }
 L 400 500 184 500 3 0 0 0 -1 -1
-L 600 200 564 272 3 0 0 0 -1 -1
-L 600 200 528 236 3 0 0 0 -1 -1
-L 528 236 564 272 3 0 0 0 -1 -1
 T 900 500 8 10 1 1 0 0 1
 refdes=Q?
+p 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 1
+M 510,240 L 601,200 L 555,295 L 535,265 z
diff --git a/symbols/analog/npn-IGBT-1.sym b/symbols/analog/npn-IGBT-1.sym
index f81c4a7..e171f11 100644
--- a/symbols/analog/npn-IGBT-1.sym
+++ b/symbols/analog/npn-IGBT-1.sym
@@ -1,4 +1,4 @@
-v 20031231 1
+v 20080706 1
 L 300 800 300 200 3 0 0 0 -1 -1
 T 100 1100 8 10 0 0 0 0 1
 device=NPN_TRANSISTOR_IGBT
@@ -10,9 +10,9 @@ T 100 550 5 6 1 1 0 0 1
 pinnumber=G
 T 100 550 5 6 0 0 0 0 1
 pinseq=2
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pinlabel=G
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pintype=pas
 }
 P 600 1000 600 800 1 0 0
@@ -21,9 +21,9 @@ T 500 850 5 6 1 1 0 0 1
 pinnumber=C
 T 500 850 5 6 0 0 0 0 1
 pinseq=1
-T 500 850 5 6 0 1 0 0 1 
+T 500 850 5 6 0 1 0 0 1
 pinlabel=C
-T 500 850 5 6 0 1 0 0 1 
+T 500 850 5 6 0 1 0 0 1
 pintype=pas
 }
 P 600 200 600 0 1 0 1
@@ -32,14 +32,13 @@ T 500 50 5 6 1 1 0 0 1
 pinnumber=E
 T 500 50 5 6 0 0 0 0 1
 pinseq=3
-T 500 50 5 6 0 1 0 0 1 
+T 500 50 5 6 0 1 0 0 1
 pinlabel=E
-T 500 50 5 6 0 1 0 0 1 
+T 500 50 5 6 0 1 0 0 1
 pintype=pas
 }
-L 600 200 550 300 3 0 0 0 -1 -1
-L 600 200 500 250 3 0 0 0 -1 -1
-L 500 250 550 300 3 0 0 0 -1 -1
 T 700 500 8 10 1 1 0 0 1
 refdes=Q?
 L 200 800 200 200 3 0 0 0 -1 -1
+p 3 0 0 0 -1 -1 2 -1 -1 -1 -1 -1 1
+M 510,240 L 601,200 L 555,295 L 535,265 z
diff --git a/symbols/analog/pnp-1.sym b/symbols/analog/pnp-1.sym
index 2c7df75..03d915c 100644
--- a/symbols/analog/pnp-1.sym
+++ b/symbols/analog/pnp-1.sym
@@ -1,4 +1,4 @@
-v 20031231 1
+v 20080706 1
 L 200 800 200 200 3 0 0 0 -1 -1
 T 600 500 5 10 0 0 0 0 1
 device=PNP_TRANSISTOR
@@ -10,9 +10,9 @@ T 100 550 5 6 1 1 0 0 1
 pinnumber=B
 T 100 550 5 6 0 0 0 0 1
 pinseq=2
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pinlabel=B
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 1000 500 800 1 0 0
@@ -21,9 +21,9 @@ T 400 850 5 6 1 1 0 0 1
 pinnumber=C
 T 400 850 5 6 0 0 0 0 1
 pinseq=1
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pinlabel=C
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 200 500 0 1 0 1
@@ -32,13 +32,12 @@ T 400 50 5 6 1 1 0 0 1
 pinnumber=E
 T 400 50 5 6 0 0 0 0 1
 pinseq=3
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pinlabel=E
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pintype=pas
 }
-L 200 500 250 400 3 0 0 0 -1 -1
-L 200 500 300 450 3 0 0 0 -1 -1
-L 250 400 300 450 3 0 0 0 -1 -1
 T 600 500 8 10 1 1 0 0 1
 refdes=Q?
+p 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 1
+M 340,290 L 300,401 L 395,355 L 365,335 z
diff --git a/symbols/analog/pnp-2.sym b/symbols/analog/pnp-2.sym
index e8968a2..f37005f 100644
--- a/symbols/analog/pnp-2.sym
+++ b/symbols/analog/pnp-2.sym
@@ -1,4 +1,4 @@
-v 20031231 1
+v 20080706 1
 L 200 800 200 200 3 0 0 0 -1 -1
 T 600 400 5 10 0 0 0 0 1
 device=PNP_TRANSISTOR
@@ -8,9 +8,9 @@ T 100 550 5 6 1 1 0 0 1
 pinnumber=B
 T 100 550 5 6 0 0 0 0 1
 pinseq=2
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pinlabel=B
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 1000 500 800 1 0 0
@@ -19,9 +19,9 @@ T 400 850 5 6 1 1 0 0 1
 pinnumber=C
 T 400 850 5 6 0 0 0 0 1
 pinseq=1
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pinlabel=C
-T 400 850 5 6 0 1 0 0 1 
+T 400 850 5 6 0 1 0 0 1
 pintype=pas
 }
 P 500 200 500 0 1 0 1
@@ -30,15 +30,14 @@ T 400 50 5 6 1 1 0 0 1
 pinnumber=E
 T 400 50 5 6 0 0 0 0 1
 pinseq=3
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pinlabel=E
-T 400 50 5 6 0 1 0 0 1 
+T 400 50 5 6 0 1 0 0 1
 pintype=pas
 }
 L 200 350 500 200 3 0 0 0 -1 -1
 L 200 650 500 800 3 0 0 0 -1 -1
-L 200 350 280 269 3 0 0 0 -1 -1
-L 280 269 312 338 3 0 0 0 -1 -1
-L 200 350 312 338 3 0 0 0 -1 -1
+p 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 1
+M 297,341 L 200,350 L 269,277 L 277,311 z
 T 600 500 8 10 1 1 0 0 1
 refdes=Q?
diff --git a/symbols/analog/pnp-3.sym b/symbols/analog/pnp-3.sym
index 60c586d..56f9f7b 100644
--- a/symbols/analog/pnp-3.sym
+++ b/symbols/analog/pnp-3.sym
@@ -1,13 +1,13 @@
-v 20031231 1
+v 20080706 1
 P 600 1000 600 800 1 0 0
 {
 T 500 850 5 6 1 1 0 0 1
 pinnumber=C
 T 500 850 5 6 0 0 0 0 1
 pinseq=1
-T 500 850 5 6 0 1 0 0 1 
+T 500 850 5 6 0 1 0 0 1
 pinlabel=C
-T 500 850 5 6 0 1 0 0 1 
+T 500 850 5 6 0 1 0 0 1
 pintype=pas
 }
 P 600 200 600 0 1 0 1
@@ -16,9 +16,9 @@ T 500 50 5 6 1 1 0 0 1
 pinnumber=E
 T 500 50 5 6 0 0 0 0 1
 pinseq=3
-T 500 50 5 6 0 1 0 0 1 
+T 500 50 5 6 0 1 0 0 1
 pinlabel=E
-T 500 50 5 6 0 1 0 0 1 
+T 500 50 5 6 0 1 0 0 1
 pintype=pas
 }
 V 500 501 316 3 0 0 0 -1 -1 0 -1 -1 -1 -1 -1
@@ -33,14 +33,13 @@ T 100 550 5 6 1 1 0 0 1
 pinnumber=B
 T 100 550 5 6 0 0 0 0 1
 pinseq=2
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pinlabel=B
-T 100 550 5 6 0 1 0 0 1 
+T 100 550 5 6 0 1 0 0 1
 pintype=pas
 }
 L 400 500 184 500 3 0 0 0 -1 -1
-L 400 400 436 328 3 0 0 0 -1 -1
-L 400 400 472 364 3 0 0 0 -1 -1
-L 472 364 436 328 3 0 0 0 -1 -1
+p 3 0 0 0 -1 -1 1 -1 -1 -1 -1 -1 1
+M 440,290 L 400,401 L 495,355 L 465,335 z
 T 900 500 8 10 1 1 0 0 1
 refdes=Q?

commit 1fffc27a3b62cc32a70edcc1ee89ff458318c6bc
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:57:52 2008 +0100

    Add support for arbitrary paths (filled and unfilled)
    
    The data-structures and on-disk format (which utiliseSVG style path
    strings) support stright line and bezier curve segments. Paths can be
    closed or open, and store line style and fill information in common with
    other geda objects.
    
    The path string parser is based on (slightly modified) code from librsvg
    version 2.22.2 (LGPL). Code from rsvg-path.c and rsvg-bpath-util.c
    (Copyright (C) 2000 Eazel, Inc.) was combined to form libgeda/src/s_path.c.
    These have been modified to use gEDA defined path data structures.
    
    The rendering implementation is based upon a small porion of the function
    rsvg_cairo_render_path (..) from rsvg-cairo-draw.c
    
    The parser still retains support the full richness of SVG path strings,
    although all instructions are subsequently normalised on to move, line, bezier
    and "close path" records. This support retained for convenience when entering
    artwork (which can be copied from an SVG editor). When saved, normalised paths
    are written out.
    
    The current rendering and shortest-distance computations are limited to
    straight line segments. Bezier segments, where encountered are treated
    as a straight line between their two endpoints.

diff --git a/gschem/include/prototype.h b/gschem/include/prototype.h
index 7fe2103..9fa556c 100644
--- a/gschem/include/prototype.h
+++ b/gschem/include/prototype.h
@@ -576,24 +576,28 @@ gboolean o_find_selected_object(GSCHEM_TOPLEVEL *w_current, int x, int y);
 OBJECT *o_grips_search_world(GSCHEM_TOPLEVEL *w_current, int x, int y, int *whichone);
 OBJECT *o_grips_search_arc_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int size, int *whichone);
 OBJECT *o_grips_search_box_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int size, int *whichone);
+OBJECT *o_grips_search_path_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int size, int *whichone);
 OBJECT *o_grips_search_picture_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int size, int *whichone);
 OBJECT *o_grips_search_circle_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int size, int *whichone);
 OBJECT *o_grips_search_line_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int size, int *whichone);
 int o_grips_start(GSCHEM_TOPLEVEL *w_current, int x, int y);
 void o_grips_start_arc(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int whichone);
 void o_grips_start_box(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int whichone);
+void o_grips_start_path(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int whichone);
 void o_grips_start_picture(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int whichone);
 void o_grips_start_circle(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int whichone);
 void o_grips_start_line(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int x, int y, int whichone);
 void o_grips_motion(GSCHEM_TOPLEVEL *w_current, int x, int y);
 void o_grips_motion_arc(GSCHEM_TOPLEVEL *w_current, int x, int y, int whichone);
 void o_grips_motion_box(GSCHEM_TOPLEVEL *w_current, int x, int y, int whichone);
+void o_grips_motion_path(GSCHEM_TOPLEVEL *w_current, int x, int y, int whichone);
 void o_grips_motion_picture(GSCHEM_TOPLEVEL *w_current, int x, int y, int whichone);
 void o_grips_motion_circle(GSCHEM_TOPLEVEL *w_current, int x, int y, int whichone);
 void o_grips_motion_line(GSCHEM_TOPLEVEL *w_current, int x, int y, int whichone);
 void o_grips_end(GSCHEM_TOPLEVEL *w_current);
 void o_grips_end_arc(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone);
 void o_grips_end_box(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone);
+void o_grips_end_path(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone);
 void o_grips_end_picture(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone);
 void o_grips_end_circle(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone);
 void o_grips_end_line(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone);
@@ -675,6 +679,17 @@ void o_picture_exchange(GSCHEM_TOPLEVEL *w_current, GdkPixbuf *pixbuf, const gch
 void picture_change_filename_dialog (GSCHEM_TOPLEVEL *w_current);
 void o_picture_set_pixbuf(GSCHEM_TOPLEVEL *w_current, GdkPixbuf *pixbuf, char *filename);
 
+/* o_path.c */
+void o_path_draw(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current);
+void o_path_eraserubber(GSCHEM_TOPLEVEL *w_current);
+void o_path_draw_xor(GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *o_current);
+void o_path_start(GSCHEM_TOPLEVEL *w_current, int x, int y);
+void o_path_end(GSCHEM_TOPLEVEL *w_current, int x, int y);
+void o_path_rubberpath(GSCHEM_TOPLEVEL *w_current, int x, int y);
+void o_path_rubberpath_xor(GSCHEM_TOPLEVEL *w_current);
+void o_path_draw_grips(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current);
+void o_path_erase_grips(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current);
+
 /* o_pin.c */
 void o_pin_draw(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current);
 void o_pin_draw_xor(GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *o_current);
diff --git a/gschem/po/POTFILES.in b/gschem/po/POTFILES.in
index f59346f..e3f4350 100644
--- a/gschem/po/POTFILES.in
+++ b/gschem/po/POTFILES.in
@@ -28,6 +28,7 @@ src/o_line.c
 src/o_misc.c
 src/o_move.c
 src/o_net.c
+src/o_path.c
 src/o_picture.c
 src/o_pin.c
 src/o_select.c
diff --git a/gschem/src/Makefile.am b/gschem/src/Makefile.am
index 4f35308..f9db66c 100644
--- a/gschem/src/Makefile.am
+++ b/gschem/src/Makefile.am
@@ -38,6 +38,7 @@ gschem_SOURCES = \
 	o_misc.c \
 	o_move.c \
 	o_net.c \
+	o_path.c \
 	o_picture.c \
 	o_pin.c \
 	o_place.c \
diff --git a/gschem/src/gschem.c b/gschem/src/gschem.c
index 80c6934..633d33d 100644
--- a/gschem/src/gschem.c
+++ b/gschem/src/gschem.c
@@ -174,6 +174,7 @@ void main_prog(void *closure, int argc, char *argv[])
   line_draw_func = o_line_draw;
   net_draw_func = o_net_draw;
   picture_draw_func = o_picture_draw;
+  path_draw_func = o_path_draw;
   pin_draw_func = o_pin_draw;
   text_draw_func = o_text_draw;
   load_newer_backup_func = x_fileselect_load_backup;
diff --git a/gschem/src/i_callbacks.c b/gschem/src/i_callbacks.c
index bcc1935..e5f72b8 100644
--- a/gschem/src/i_callbacks.c
+++ b/gschem/src/i_callbacks.c
@@ -1288,7 +1288,8 @@ DEFINE_I_CALLBACK(edit_linetype)
       if (o_current->type == OBJ_LINE   ||
           o_current->type == OBJ_BOX    ||
           o_current->type == OBJ_CIRCLE ||
-          o_current->type == OBJ_ARC) {
+          o_current->type == OBJ_ARC ||
+          o_current->type == OBJ_PATH) {
         objects = g_list_prepend (objects, o_current);
       }
 
@@ -1329,7 +1330,8 @@ DEFINE_I_CALLBACK(edit_filltype)
       OBJECT *o_current = (OBJECT *) s_current->data;
           
       if (o_current->type == OBJ_BOX ||
-          o_current->type == OBJ_CIRCLE) {
+          o_current->type == OBJ_CIRCLE ||
+          o_current->type == OBJ_PATH) {
         objects = g_list_prepend (objects, o_current);
       }
 
diff --git a/gschem/src/o_basic.c b/gschem/src/o_basic.c
index 212d700..4246189 100644
--- a/gschem/src/o_basic.c
+++ b/gschem/src/o_basic.c
@@ -598,6 +598,7 @@ void o_draw_xor(GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *object)
       case OBJ_PLACEHOLDER:
       case OBJ_COMPLEX: func = o_complex_draw_xor;        break;
       case OBJ_TEXT:    func = o_text_draw_xor;           break;
+      case OBJ_PATH:    func = o_path_draw_xor;           break;
       case OBJ_PIN:     func = o_pin_draw_xor;            break;
       case OBJ_ARC:     func = o_arc_draw_xor;            break;
       default:
@@ -707,6 +708,7 @@ void o_erase_grips (GSCHEM_TOPLEVEL *w_current, OBJECT *object)
       case OBJ_PIN:     func = o_line_erase_grips;    break;
       case OBJ_CIRCLE:  func = o_circle_erase_grips;  break;
       case OBJ_PICTURE: func = o_picture_erase_grips; break;
+      case OBJ_PATH:    func = o_path_erase_grips;    break;
   }
 
   if (func != NULL) {
diff --git a/gschem/src/o_grips.c b/gschem/src/o_grips.c
index 964bf25..29f8b37 100644
--- a/gschem/src/o_grips.c
+++ b/gschem/src/o_grips.c
@@ -100,6 +100,13 @@ OBJECT *o_grips_search_world(GSCHEM_TOPLEVEL *w_current, int x, int y, int *whic
           if(found != NULL) return found;
           break;
 
+        case(OBJ_PATH):
+          /* check the grips of the path object */
+          found = o_grips_search_path_world(w_current, object,
+                                            x, y, w_size, whichone);
+          if(found != NULL) return found;
+          break;
+
         case(OBJ_PICTURE):
           /* check the grips of the picture object */
           found = o_grips_search_picture_world(w_current, object,
@@ -299,6 +306,76 @@ OBJECT *o_grips_search_box_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
   return NULL;
 }
 
+/*! \brief Check if pointer is inside path grip.
+ *  \par Function Description
+ *  This function checks if the pointer event occuring at (<B>x</B>,<B>y</B>)
+ *  is inside one of the grips of the <B>o_current</B> pointed path object.
+ *  If so, the <B>whichone</B> pointed integer is set to the identifier of
+ *  this grip and the returned pointer is a pointer on this object.
+ *  If the point is not inside a grip the function returns a NULL pointer
+ *  and the <B>whichone</B> pointed integer is unset.
+ *
+ *  A path object has four grips : one at each corner of the path.
+ *  The identifiers of each corner are #PICTURE_UPPER_LEFT,
+ *  #PICTURE_UPPER_RIGHT, #PICTURE_LOWER_LEFT and
+ *  #PICTURE_LOWER_RIGHT.
+ *
+ *  The <B>x</B> and <B>y</B> parameters are in world units.
+ *
+ *  The <B>size</B> parameter is half the width (and half the height) of the
+ *  square representing a grip in world units.
+ *
+ *  \param [in]  w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in]  o_current  Picture OBJECT to check.
+ *  \param [in]  x          Current x coordinate of pointer in world units.
+ *  \param [in]  y          Current y coordinate of pointer in world units.
+ *  \param [in]  size       Half the width of the grip square in world units.
+ *  \param [out] whichone   Which grip point is selected.
+ *  \return Pointer to OBJECT the grip is on, NULL otherwise.
+ */
+OBJECT *o_grips_search_path_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
+                                     int x, int y, int size, int *whichone)
+{
+  PATH_SECTION *section;
+  int i;
+  int grip_no = 0;
+
+  for (i = 0; i <  o_current->path->num_sections; i++) {
+    section = &o_current->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* inside first control grip ? */
+      if (inside_grip(x, y, section->x1, section->y1, size)) {
+        *whichone = grip_no;
+        return o_current;
+      }
+      grip_no ++;
+      /* inside second control grip ? */
+      if (inside_grip(x, y, section->x2, section->y2, size)) {
+        *whichone = grip_no;
+        return o_current;
+      }
+      grip_no ++;
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      /* inside destination control grip ? */
+      if (inside_grip(x, y, section->x3, section->y3, size)) {
+        *whichone = grip_no;
+        return o_current;
+      }
+      grip_no ++;
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+
+  return NULL;
+}
+
 /*! \brief Check if pointer is inside picture grip.
  *  \par Function Description
  *  This function checks if the pointer event occuring at (<B>x</B>,<B>y</B>)
@@ -497,6 +574,11 @@ int o_grips_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
       o_grips_start_box(w_current, object, w_x, w_y, whichone);
       return(TRUE);
 
+    case(OBJ_PATH):
+      /* start the modification of a grip on a path */
+      o_grips_start_path(w_current, object, w_x, w_y, whichone);
+      return(TRUE);
+
     case(OBJ_PICTURE):
       /* start the modification of a grip on a picture */
       o_grips_start_picture(w_current, object, w_x, w_y, whichone);
@@ -636,6 +718,79 @@ void o_grips_start_box(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
   w_current->rubber_visible = 1;
 }
 
+/*! \brief Initialize grip motion process for a path.
+ *  \par Function Description
+ *  This function initializes the grip motion process for a path.
+ *  From the <B>o_current</B> pointed object, it stores into the
+ *  GSCHEM_TOPLEVEL structure the ....
+ *  These variables are used in the grip process.
+ *
+ *  The function first erases the grips.
+ *
+ *  The coordinates of the selected corner are put in
+ *  (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>).
+ *
+ *  The coordinates of the opposite corner go in
+ *  (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>). They are not
+ *  suppose to change during the action.
+ *
+ *  \param [in]  w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in]  o_current  Picture OBJECT to check.
+ *  \param [in]  x          (unused)
+ *  \param [in]  y          (unused)
+ *  \param [out] whichone   Which coordinate to check.
+ */
+void o_grips_start_path(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
+                           int x, int y, int whichone)
+{
+  PATH_SECTION *section;
+  int i;
+  int grip_no = 0;
+  int gx = -1;
+  int gy = -1;
+
+  w_current->last_drawb_mode = -1;
+
+  /* erase the path before */
+  o_erase_single(w_current, o_current);
+
+  for (i = 0; i <  o_current->path->num_sections; i++) {
+    section = &o_current->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* Two control point grips */
+      if (whichone == grip_no++) {
+        gx = section->x1;
+        gy = section->y1;
+      }
+      if (whichone == grip_no++) {
+        gx = section->x2;
+        gy = section->y2;
+      }
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      /* Destination point grip */
+      if (whichone == grip_no++) {
+        gx = section->x3;
+        gy = section->y3;
+      }
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+
+  w_current->first_wx = w_current->second_wx = gx;
+  w_current->first_wy = w_current->second_wy = gy;
+
+  /* draw the first temporary path */
+  o_path_rubberpath_xor(w_current);
+  w_current->rubber_visible = 1;
+}
+
 /*! \brief Initialize grip motion process for a picture.
  *  \par Function Description
  *  This function initializes the grip motion process for a picture.
@@ -824,6 +979,11 @@ void o_grips_motion(GSCHEM_TOPLEVEL *w_current, int unsnapped_wx, int unsnapped_
     o_grips_motion_box (w_current, w_x, w_y, grip);
     break;
 
+    case(OBJ_PATH):
+    /* erase, update and draw a box */
+    o_grips_motion_path(w_current, w_x, w_y, grip);
+    break;
+
     case(OBJ_PICTURE):
     /* erase, update and draw a box */
     o_grips_motion_picture (w_current, w_x, w_y, grip);
@@ -902,6 +1062,31 @@ void o_grips_motion_box(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y, int whicho
   o_box_rubberbox(w_current, w_x, w_y);
 }
 
+/*! \brief Modify previously selected path according to mouse position.
+ *  \par Function Description
+ *  This function is the refreshing part of the grip motion process. It is
+ *  called whenever the position of the pointer is changed, therefore
+ *  requiring the GSCHEM_TOPLEVEL variables to be updated.
+ *  Depending on the grip selected and moved, the temporary GSCHEM_TOPLEVEL
+ *  variables are changed according to the current position of the pointer
+ *  and the modifications temporary drawn.
+ *
+ *  This function only makes a call to #o_path_rubberbox() that
+ *  updates the GSCHEM_TOPLEVEL variables, erase the previous temporary
+ *   path and draw the new temporary path.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] w_x        Current x coordinate of pointer in world units.
+ *  \param [in] w_y        Current y coordinate of pointer in world units.
+ *  \param [in] whichone   Which grip to start motion with.
+ */
+void o_grips_motion_path (GSCHEM_TOPLEVEL *w_current,
+                          int w_x, int w_y, int whichone)
+{
+  /* erase, update and draw the temporary path */
+  o_path_rubberpath(w_current, w_x, w_y);
+}
+
 /*! \brief Modify previously selected picture according to mouse position.
  *  \par Function Description
  *  This function is the refreshing part of the grip motion process. It is
@@ -1011,6 +1196,11 @@ void o_grips_end(GSCHEM_TOPLEVEL *w_current)
     o_grips_end_box(w_current, object, grip);
     break;
 
+    case(OBJ_PATH):
+    /* modify a path object */
+    o_grips_end_path(w_current, object, grip);
+    break;
+
     case(OBJ_PICTURE):
     /* modify a picture object */
     o_grips_end_picture(w_current, object, grip);
@@ -1152,6 +1342,27 @@ void o_grips_end_box(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone
 }
 
 /*! \todo Finish function documentation!!!
+ *  \brief End process of modifying path object with grip.
+ *  \par Function Description
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] o_current  Picture OBJECT to end modification on.
+ *  \param [in] whichone   Which grip is pointed to.
+ */
+void o_grips_end_path(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone)
+{
+  /* erase the temporary path */
+  if (w_current->rubber_visible)
+    o_path_rubberpath_xor(w_current);
+
+  o_path_modify (w_current->toplevel, o_current,
+                 w_current->second_wx, w_current->second_wy, whichone);
+
+  /* draw the modified path */
+  o_redraw_single(w_current, o_current);
+}
+
+/*! \todo Finish function documentation!!!
  *  \brief End process of modifying picture object with grip.
  *  \par Function Description
  *
diff --git a/gschem/src/o_path.c b/gschem/src/o_path.c
new file mode 100644
index 0000000..8ca7ffa
--- /dev/null
+++ b/gschem/src/o_path.c
@@ -0,0 +1,550 @@
+/* gEDA - GPL Electronic Design Automation
+ * gschem - gEDA Schematic Capture
+ * Copyright (C) 1998-2007 Ales Hvezda
+ * Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <math.h>
+
+#include "gschem.h"
+
+#ifdef HAVE_LIBDMALLOC
+#include <dmalloc.h>
+#endif
+
+
+static void path_to_points_modify (GSCHEM_TOPLEVEL *w_current, PATH *path,
+                                   int dx, int dy, int new_x, int new_y, int whichone,
+                                   GdkPoint **points, int *num_points)
+
+{
+  TOPLEVEL *toplevel = w_current->toplevel;
+  PATH_SECTION *section;
+  int x1, y1, x2, y2, x3, y3;
+  int i;
+  int grip_no = 0;
+
+  *points = g_new0 (GdkPoint, path->num_sections);
+
+  *num_points = 0;
+
+  for (i = 0; i <  path->num_sections; i++) {
+    section = &path->sections[i];
+
+    x1 = section->x1 + dx; y1 = section->y1 + dy;
+    x2 = section->x2 + dx; y2 = section->y2 + dy;
+    x3 = section->x3 + dx; y3 = section->y3 + dy;
+
+    switch (section->code) {
+      case PATH_CURVETO:
+        /* Two control point grips */
+        if (whichone == grip_no++) {
+          x1 = new_x; y1 = new_y;
+        }
+        if (whichone == grip_no++) {
+          x2 = new_x; y2 = new_y;
+        }
+        WORLDtoSCREEN (toplevel, x1, y1, &x1, &y1);
+        WORLDtoSCREEN (toplevel, x2, y2, &x2, &y2);
+        /* Fall through */
+      case PATH_MOVETO:
+      case PATH_MOVETO_OPEN:
+      case PATH_LINETO:
+        /* Destination point grip */
+        if (whichone == grip_no++) {
+          x3 = new_x; y3 = new_y;
+        }
+        WORLDtoSCREEN (toplevel, x3, y3, &x3, &y3);
+      case PATH_END:
+        break;
+    }
+
+    switch (section->code) {
+      case PATH_CURVETO:
+        /* Unsupported, just fall through and draw a line */
+        /* Fall through */
+      case PATH_MOVETO_OPEN:
+        /* Unsupported, just fall through and draw a line */
+        /* Fall through */
+      case PATH_MOVETO:
+      case PATH_LINETO:
+        (*points)[*num_points].x = x3;
+        (*points)[*num_points].y = y3;
+        (*num_points)++;
+        break;
+      case PATH_END:
+        break;
+    }
+  }
+}
+
+
+static void path_to_points (GSCHEM_TOPLEVEL *w_current, PATH *path,
+                            int dx, int dy,
+                            GdkPoint **points, int *num_points)
+{
+  path_to_points_modify (w_current, path,
+                         dx, dy, 0, 0, -1,
+                         points, num_points);
+}
+
+
+static void find_points_bounds (GdkPoint *points, int num_points,
+                                int *min_x, int *min_y, int *max_x, int *max_y)
+{
+  int i;
+  int found_bound = FALSE;
+
+  for (i = 0; i < num_points; i++) {
+    *min_x = (found_bound) ? min (*min_x, points[i].x) : points[i].x;
+    *min_y = (found_bound) ? min (*min_y, points[i].y) : points[i].y;
+    *max_x = (found_bound) ? max (*max_x, points[i].x) : points[i].x;
+    *max_y = (found_bound) ? max (*max_y, points[i].y) : points[i].y;
+    found_bound = TRUE;
+  }
+}
+
+
+/*! \brief Draw a path on screen.
+ *  \par Function Description
+ *  This function is used to draw a path on screen. The path is described
+ *  in the object which is referred by <B>o_current</B>. The path is displayed
+ *  according to the current state, described in the GSCHEM_TOPLEVEL object pointed
+ *  by <B>w_current</B>.
+ *
+ *  It first checks if the object is valid or not. If not it returns and do
+ *  not output anything. That should never happen though.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] o_current  The path OBJECT to draw.
+ */
+void o_path_draw(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
+{
+  TOPLEVEL *toplevel = w_current->toplevel;
+  PATH *path = o_current->path;
+  int line_width, length, space;
+  int wleft, wtop, wright, wbottom;
+  GdkPoint *points;
+  int num_points;
+
+  GdkColor *color;
+  GdkCapStyle path_end;
+
+  if (path == NULL) {
+    return;
+  }
+
+  world_get_single_object_bounds(toplevel, o_current,
+                                 &wleft, &wtop, &wright, &wbottom);
+  if ( (toplevel->DONT_REDRAW == 1) ||
+       (!visible(toplevel, wleft, wtop, wright, wbottom)) ) {
+    return;
+  }
+
+  if (toplevel->override_color != -1 )
+    color = x_get_color(toplevel->override_color);
+  else
+    color = x_get_color(o_current->color);
+
+  line_width = SCREENabs( toplevel, o_current->line_width );
+  if( line_width <= 0) {
+    line_width = 1;
+  }
+
+  switch(o_current->line_end) {
+    case END_NONE:   path_end = GDK_CAP_BUTT;       break;
+    case END_SQUARE: path_end = GDK_CAP_PROJECTING; break;
+    case END_ROUND:  path_end = GDK_CAP_ROUND;      break;
+    default:
+      fprintf(stderr, _("Unknown end for path (%d)\n"),
+                      o_current->line_end);
+      path_end = GDK_CAP_BUTT;
+    break;
+  }
+
+  length = SCREENabs( toplevel, o_current->line_length );
+  space = SCREENabs( toplevel, o_current->line_space );
+
+  path_to_points (w_current, path, 0, 0, &points, &num_points);
+
+  if (num_points == 0) {
+    g_free (points);
+    return;
+  }
+
+  /* TODO: Currently we only support solid line drawing */
+  gdk_gc_set_foreground(w_current->gc, color);
+  gdk_gc_set_line_attributes(w_current->gc, line_width, GDK_LINE_SOLID,
+                             path_end, GDK_JOIN_MITER);
+
+  /* Stroke */
+  if (path->sections[path->num_sections - 1].code == PATH_END)
+    gdk_draw_polygon (w_current->backingstore, w_current->gc,
+                      FALSE, points, num_points);
+  else
+    gdk_draw_lines (w_current->backingstore, w_current->gc,
+                    points, num_points);
+
+  /* TODO: Currently we only support solid fill drawing */
+  /* Fill */
+  if (o_current->fill_type != FILLING_HOLLOW)
+    gdk_draw_polygon(w_current->backingstore, w_current->gc,
+                     TRUE, points, num_points);
+
+  /* reset line width and reset back to default */
+  gdk_gc_set_line_attributes(w_current->gc, 0, GDK_LINE_SOLID,
+                             GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
+
+  g_free (points);
+
+  if (o_current->draw_grips && w_current->draw_grips == TRUE) {
+    if (!o_current->selected) {
+      /* object is no more selected, erase the grips */
+      o_current->draw_grips = FALSE;
+      o_path_erase_grips(w_current, o_current);
+    } else {
+      /* object is selected, draw the grips */
+      o_path_draw_grips(w_current, o_current);
+    }
+  }
+
+}
+
+
+/*! \todo Finish function documentation
+ *  \brief
+ *  \par Function Description
+ *
+ *  \note
+ *  used in button cancel code in x_events.c
+ */
+void o_path_eraserubber(GSCHEM_TOPLEVEL *w_current)
+{
+  o_path_rubberpath_xor (w_current);
+}
+
+
+/*! \brief Draw a path object after applying translation.
+ *  \par Function Description
+ *  This function is used to draw the path object described by
+ *  <B>*o_current</B> after applying a translation on the two directions of
+ *  <B>dx</B> and <B>dy</B> in world units. It uses and XOR function to draw the
+ *  translated path over the current sheet.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] dx         Delta x coordinate for path.
+ *  \param [in] dy         Delta y coordinate for path.
+ *  \param [in] o_current  Line OBJECT to draw.
+ */
+void o_path_draw_xor(GSCHEM_TOPLEVEL *w_current, int dx, int dy, OBJECT *o_current)
+{
+  PATH *path = o_current->path;
+  int color;
+  int num_points;
+  GdkPoint *points;
+
+  path_to_points (w_current, path, dx, dy, &points, &num_points);
+
+  if (num_points == 0) {
+    g_free (points);
+    return;
+  }
+
+  if (o_current->saved_color != -1) {
+    color = o_current->saved_color;
+  } else {
+    color = o_current->color;
+  }
+
+  gdk_gc_set_foreground(w_current->outline_xor_gc,
+                        x_get_darkcolor(color));
+  gdk_gc_set_line_attributes(w_current->xor_gc, 0, GDK_LINE_SOLID,
+                             GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
+
+  /* Stroke only, no fill for XOR */
+  if (path->sections[path->num_sections - 1].code == PATH_END)
+    gdk_draw_polygon (w_current->backingstore, w_current->xor_gc,
+                      FALSE, points, num_points);
+  else
+    gdk_draw_lines (w_current->backingstore, w_current->xor_gc,
+                    points, num_points);
+
+  g_free (points);
+}
+
+/*! \brief Start process to input a new path.
+ *  \par Function Description
+ *  This function starts the process of interactively adding a path to
+ *  the current sheet.
+ *
+ *  During all the process, the path is internally represented by the two
+ *  ends of the path as (<B>w_current->first_wx</B>,<B>w_current->first_wy</B>) and
+ *  (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>).
+ *
+ *  A temporary path is xor-drawn during the process with the selection color
+ *  and changed according to the position of the mouse pointer.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] w_x        Current x coordinate of pointer in world units.
+ *  \param [in] w_y        Current y coordinate of pointer in world units.
+ */
+void o_path_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
+{
+  /* TODO: Implement support for drawing paths from within gschem */
+}
+
+
+/*! \brief End the input of a path.
+ *  \par Function Description
+ *  This function ends the process of interactively adding a path to the
+ *  current sheet.
+ *
+ *  It first erases the last temporary path displayed, calculates the
+ *  corresponding world coordinates of the two ends of the path and finally
+ *  adds a new initialized path object to the list of object of the current
+ *  sheet.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] w_x        (unused)
+ *  \param [in] w_y        (unused)
+ */
+void o_path_end(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
+{
+  /* TODO: Implement support for drawing paths from within gschem */
+}
+
+
+/*! \brief Draw temporary path while dragging end.
+ *  \par Function Description
+ *  This function manages the erase/update/draw process of temporary path
+ *  when modifying one end of the path.
+ *  The path is described by four <B>*w_current</B> variables : the first end
+ *  of the path is (<B>first_wx</B>,<B>first_wy</B>), the second end is
+ *  (<B>second_wx</B>,<B>second_wy</B>).
+ *  The first end is constant. The second end is updated to the (<B>w_x</B>,<B>w_y</B>).
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] w_x        Current x coordinate of pointer in world units.
+ *  \param [in] w_y        Current y coordinate of pointer in world units.
+ */
+void o_path_rubberpath(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
+{
+  if (w_current->rubber_visible)
+    o_path_rubberpath_xor (w_current);
+
+  w_current->second_wx = w_x;
+  w_current->second_wy = w_y;
+
+  o_path_rubberpath_xor (w_current);
+  w_current->rubber_visible = 1;
+}
+
+
+/*! \brief Draw path from GSCHEM_TOPLEVEL object.
+ *  \par Function Description
+ *  This function draws a path with an exclusive or function over the sheet.
+ *  The color of the box is <B>w_current->select_color</B>. The path is
+ *  described by the two points (<B>w_current->first_wx</B>,
+ *  <B>w_current->first_wy</B>) and (<B>w_current->second_wx</B>,<B>w_current->second_wy</B>).
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ */
+void o_path_rubberpath_xor(GSCHEM_TOPLEVEL *w_current)
+{
+  PATH *path;
+  int num_points;
+  GdkPoint *points;
+  int left = 0, top = 0, right = 0, bottom = 0;
+
+  g_return_if_fail (w_current->which_object != NULL);
+  g_return_if_fail (w_current->which_object->path != NULL);
+
+  path = w_current->which_object->path;
+
+  path_to_points_modify (w_current, path, 0, 0,
+                         w_current->second_wx, w_current->second_wy,
+                         w_current->which_grip, &points, &num_points);
+
+  if (num_points == 0) {
+    g_free (points);
+    return;
+  }
+
+  gdk_gc_set_foreground(w_current->xor_gc,
+                        x_get_darkcolor(w_current->select_color));
+  gdk_gc_set_line_attributes(w_current->xor_gc, 0, GDK_LINE_SOLID,
+                             GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
+
+  /* Stroke only, no fill for rubberbanding */
+  if (path->sections[path->num_sections - 1].code == PATH_END)
+    gdk_draw_polygon (w_current->backingstore, w_current->xor_gc,
+                      FALSE, points, num_points);
+  else
+    gdk_draw_lines (w_current->backingstore, w_current->xor_gc,
+                    points, num_points);
+
+  find_points_bounds (points, num_points, &left, &top, &right, &bottom);
+  o_invalidate_rect (w_current, left, top, right, bottom);
+
+  g_free (points);
+}
+
+
+/*! \brief Draw lines between curve segment end-point and their control point.
+ *
+ *  \par Function Description
+ *  This function XOR draws lines between the end-points and respective
+ *  control-points of curve segments in the path.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] o_current  The path OBJECT.
+ */
+static void o_path_xor_control_lines (GSCHEM_TOPLEVEL *w_current,
+                                      OBJECT *o_current)
+{
+  TOPLEVEL *toplevel = w_current->toplevel;
+  int i;
+  int x, y;
+  int next_x, next_y;
+  int last_x = 0, last_y = 0;
+  PATH_SECTION *section;
+
+  gdk_gc_set_foreground(w_current->outline_xor_gc,
+                        x_get_darkcolor(w_current->select_color));
+
+  for (i = 0; i <  o_current->path->num_sections; i++) {
+    section = &o_current->path->sections[i];
+
+    if (section->code != PATH_END)
+      WORLDtoSCREEN (toplevel, section->x3, section->y3, &next_x, &next_y);
+
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* Two control point grips */
+      WORLDtoSCREEN (toplevel, section->x1, section->y1, &x, &y);
+      gdk_draw_line (w_current->backingstore, w_current->outline_xor_gc,
+                     last_x, last_y, x, y);
+      WORLDtoSCREEN (toplevel, section->x2, section->y2, &x, &y);
+      gdk_draw_line (w_current->backingstore, w_current->outline_xor_gc,
+                     next_x, next_y, x, y);
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      last_x = next_x;
+      last_y = next_y;
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+}
+
+
+/*! \brief Draw grip marks on path.
+ *  \par Function Description
+ *  This function draws the grips on the path object <B>o_current</B>.
+ *
+ *  A path has a grip at each end.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] o_current  Line OBJECT to draw grip points on.
+ */
+void o_path_draw_grips(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
+{
+  TOPLEVEL *toplevel = w_current->toplevel;
+  PATH_SECTION *section;
+  int i;
+  int x, y;
+
+  if (w_current->draw_grips == FALSE)
+    return;
+
+  o_path_xor_control_lines (w_current, o_current);
+
+  for (i = 0; i <  o_current->path->num_sections; i++) {
+    section = &o_current->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* Two control point grips */
+      WORLDtoSCREEN (toplevel, section->x1, section->y1, &x, &y);
+      o_grips_draw (w_current, x, y);
+      WORLDtoSCREEN (toplevel, section->x2, section->y2, &x, &y);
+      o_grips_draw (w_current, x, y);
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      /* Destination point grip */
+      WORLDtoSCREEN (toplevel, section->x3, section->y3, &x, &y);
+      o_grips_draw (w_current, x, y);
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+}
+
+
+/*! \brief Erase grip marks from path.
+ *  \par Function Description
+ *  This function erases the grips on the path object <B>o_current</B>.
+ *
+ *  A path has a grip at each end.
+ *
+ *  \param [in] w_current  The GSCHEM_TOPLEVEL object.
+ *  \param [in] o_current  Line OBJECT to erase grip marks from.
+ */
+void o_path_erase_grips(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current)
+{
+  TOPLEVEL *toplevel = w_current->toplevel;
+  PATH_SECTION *section;
+  int i;
+  int x, y;
+
+  if (w_current->draw_grips == FALSE)
+    return;
+
+  for (i = 0; i <  o_current->path->num_sections; i++) {
+    section = &o_current->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* Two control point grips */
+      WORLDtoSCREEN (toplevel, section->x1, section->y1, &x, &y);
+      o_grips_erase (w_current, x, y);
+      WORLDtoSCREEN (toplevel, section->x2, section->y2, &x, &y);
+      o_grips_erase (w_current, x, y);
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      /* Destination point grip */
+      WORLDtoSCREEN (toplevel, section->x3, section->y3, &x, &y);
+      o_grips_erase (w_current, x, y);
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+
+  o_path_xor_control_lines (w_current, o_current);
+}
diff --git a/libgeda/include/funcs.h b/libgeda/include/funcs.h
index 11cbb2f..b6b2fbd 100644
--- a/libgeda/include/funcs.h
+++ b/libgeda/include/funcs.h
@@ -8,6 +8,7 @@ extern void (*net_draw_func)();
 extern void (*bus_draw_func)();
 extern void (*text_draw_func)();
 extern void (*pin_draw_func)();
+extern void (*path_draw_func)();
 extern void (*select_func)();
 extern void (*x_log_update_func)();
 /* load_newer_backup_func is called if an autosave backup file is found
diff --git a/libgeda/include/o_types.h b/libgeda/include/o_types.h
index ba0b25b..bd5e4b5 100644
--- a/libgeda/include/o_types.h
+++ b/libgeda/include/o_types.h
@@ -26,6 +26,7 @@
    (function g_register_libgeda_vars) */
 #define OBJ_HEAD       	-1 
 #define OBJ_LINE        'L'
+#define OBJ_PATH        'p'
 #define OBJ_BOX         'B'
 #define OBJ_PICTURE     'G'
 #define OBJ_CIRCLE      'V'
diff --git a/libgeda/include/prototype.h b/libgeda/include/prototype.h
index 76cd6aa..ecb19d0 100644
--- a/libgeda/include/prototype.h
+++ b/libgeda/include/prototype.h
@@ -247,6 +247,14 @@ int o_net_consolidate_segments(TOPLEVEL *toplevel, OBJECT *object);
 void o_net_consolidate(TOPLEVEL *toplevel);
 void o_net_modify(TOPLEVEL *toplevel, OBJECT *object, int x, int y, int whichone);
 
+/* o_path_basic.c */
+OBJECT *o_path_add(TOPLEVEL *toplevel, OBJECT *object_list, char type, int color, const char *path_string);
+OBJECT *o_path_copy(TOPLEVEL *toplevel, OBJECT *list_tail, OBJECT *o_current);
+void o_path_modify(TOPLEVEL *toplevel, OBJECT *object, int x, int y, int whichone);
+void o_path_translate_world(TOPLEVEL *toplevel, int x, int y, OBJECT *object);
+void o_path_rotate_world(TOPLEVEL *toplevel, int world_centerx, int world_centery, int angle, OBJECT *object);
+void o_path_mirror_world(TOPLEVEL *toplevel, int world_centerx, int world_centery, OBJECT *object);
+
 /* o_picture.c */
 OBJECT *o_picture_add(TOPLEVEL *toplevel, OBJECT *list_tail, GdkPixbuf *pixbuf,
                       gchar *file_content, gsize file_length, char *filename,
@@ -428,6 +436,10 @@ void s_papersizes_init(void);
 char *s_papersizes_get(int counter);
 void s_papersizes_get_size(char *string, int *width, int *height);
 
+/* s_path.c */
+PATH *s_path_parse (const char *path_str);
+char *s_path_string_from_path (const PATH *path);
+
 /* s_project.c */
 TOPLEVEL *s_toplevel_new (void);
 void s_toplevel_delete (TOPLEVEL *toplevel);
diff --git a/libgeda/include/prototype_priv.h b/libgeda/include/prototype_priv.h
index 4f585af..659f6c4 100644
--- a/libgeda/include/prototype_priv.h
+++ b/libgeda/include/prototype_priv.h
@@ -142,6 +142,15 @@ void o_net_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current, int origin_x,
 void world_get_net_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, int *top, int *right, int *bottom);
 void o_net_recalc(TOPLEVEL *toplevel, OBJECT *o_current);
 
+/* o_path_basic.c */
+OBJECT *o_path_read(TOPLEVEL *toplevel, OBJECT *object_list, const char *first_line, TextBuffer *tb, unsigned int release_ver, unsigned int fileformat_ver);
+char *o_path_save(OBJECT *object);
+void o_path_print(TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current, int origin_x, int origin_y);
+gdouble o_path_shortest_distance(OBJECT *object, gint x, gint y);
+void world_get_path_bounds(TOPLEVEL *toplevel, OBJECT *object, int *left, int *top, int *right, int *bottom);
+void o_path_recalc(TOPLEVEL *toplevel, OBJECT *o_current);
+
+
 /* o_picture.c */
 OBJECT *o_picture_read(TOPLEVEL *toplevel, OBJECT *object_list,
 		       const char *first_line,
diff --git a/libgeda/include/struct.h b/libgeda/include/struct.h
index 4224034..3636802 100644
--- a/libgeda/include/struct.h
+++ b/libgeda/include/struct.h
@@ -29,6 +29,8 @@ typedef struct _GedaList GedaPageList;
 /* gschem structures (gschem) */
 typedef struct st_complex COMPLEX;
 typedef struct st_line LINE;
+typedef struct st_path_section PATH_SECTION;
+typedef struct st_path PATH;
 typedef struct st_circle CIRCLE;
 typedef struct st_arc ARC;
 typedef struct st_box BOX;
@@ -96,6 +98,30 @@ struct st_line {
 #define LINE_END1 0
 #define LINE_END2 1
 
+typedef enum {
+    PATH_MOVETO,
+    PATH_MOVETO_OPEN,
+    PATH_CURVETO,
+    PATH_LINETO,
+    PATH_END
+} PATH_CODE;
+
+struct st_path_section {
+  PATH_CODE code;
+  int x1;
+  int y1;
+  int x2;
+  int y2;
+  int x3;
+  int y3;
+};
+
+struct st_path {
+  PATH_SECTION *sections; /* Bezier path segments  */
+  int num_sections;       /* Number with data      */
+  int num_sections_max;   /* Number allocated      */
+};
+
 struct st_arc {
   int x, y; /* world */
 
@@ -204,6 +230,7 @@ struct st_object {
   BOX *box;
   TEXT *text;
   PICTURE *picture;
+  PATH *path;
 
   GList *tiles;			/* tiles */
 
diff --git a/libgeda/po/POTFILES.in b/libgeda/po/POTFILES.in
index 5161824..6e70553 100644
--- a/libgeda/po/POTFILES.in
+++ b/libgeda/po/POTFILES.in
@@ -18,6 +18,7 @@ src/o_complex_basic.c
 src/o_embed.c
 src/o_line_basic.c
 src/o_net_basic.c
+src/o_path_basic.c
 src/o_picture.c
 src/o_pin_basic.c
 src/o_text_basic.c
diff --git a/libgeda/src/Makefile.am b/libgeda/src/Makefile.am
index 55c80ce..bcae051 100644
--- a/libgeda/src/Makefile.am
+++ b/libgeda/src/Makefile.am
@@ -36,6 +36,7 @@ libgeda_la_SOURCES = \
 	o_line_basic.c \
 	o_list.c \
 	o_net_basic.c \
+	o_path_basic.c \
 	o_picture.c \
 	o_pin_basic.c \
 	o_selection.c \
@@ -52,6 +53,7 @@ libgeda_la_SOURCES = \
 	s_menu.c \
 	s_page.c \
 	s_papersizes.c \
+	s_path.c \
 	s_slib.c \
 	s_stretch.c \
 	s_textbuffer.c \
diff --git a/libgeda/src/a_basic.c b/libgeda/src/a_basic.c
index 4d10ef8..5d50f26 100644
--- a/libgeda/src/a_basic.c
+++ b/libgeda/src/a_basic.c
@@ -153,6 +153,10 @@ gchar *o_save_objects (OBJECT *object_list)
             out = (char *) o_text_save(o_current);
             break;
 
+          case(OBJ_PATH):
+            out = (char *) o_path_save(o_current);
+            break;
+
           case(OBJ_PIN):
             out = (char *) o_pin_save(o_current);
             break;
@@ -350,6 +354,13 @@ OBJECT *o_read_buffer(TOPLEVEL *toplevel, OBJECT *object_list,
 	g_free(line);
         break;
 
+      case(OBJ_PATH):
+        line = g_strdup(line);
+        object_list = (OBJECT *) o_path_read(toplevel, object_list, line, tb,
+                                             release_ver, fileformat_ver);
+        g_free (line);
+        break;
+
       case(OBJ_PIN):
         object_list = (OBJECT *) o_pin_read(toplevel, object_list, line,
                                             release_ver, fileformat_ver);
diff --git a/libgeda/src/f_print.c b/libgeda/src/f_print.c
index 97b9635..61e6924 100644
--- a/libgeda/src/f_print.c
+++ b/libgeda/src/f_print.c
@@ -314,6 +314,11 @@ void f_print_objects(TOPLEVEL *toplevel, FILE *fp, OBJECT *head,
           break;
 
 
+        case(OBJ_PATH):
+          o_path_print(toplevel, fp, o_current,
+                       origin_x, origin_y);
+          break;
+
         case(OBJ_PIN):
           o_pin_print(toplevel, fp, o_current,
                       origin_x, origin_y);
diff --git a/libgeda/src/o_attrib.c b/libgeda/src/o_attrib.c
index b67a770..a967339 100644
--- a/libgeda/src/o_attrib.c
+++ b/libgeda/src/o_attrib.c
@@ -366,6 +366,15 @@ OBJECT *o_read_attribs(TOPLEVEL *toplevel,
                                              object_list);
         break;
 
+      case(OBJ_PATH):
+        line = g_strdup (line);
+        object_list = (OBJECT *) o_path_read(toplevel,
+                                             object_list,
+                                             line, tb,
+                                             release_ver, fileformat_ver);
+        g_free (line);
+        break;
+
       case(OBJ_PIN):
         object_list = (OBJECT *) o_pin_read(toplevel,
                                             object_list, 
@@ -483,6 +492,10 @@ gchar *o_save_attribs(GList *attribs)
           out = (char *) o_text_save(a_current);
           break;
 
+        case(OBJ_PATH):
+          out = (char *) o_path_save(a_current);
+          break;
+
         case(OBJ_PIN):
           out = (char *) o_pin_save(a_current);
           break;
diff --git a/libgeda/src/o_basic.c b/libgeda/src/o_basic.c
index 28ddd54..2c9f80a 100644
--- a/libgeda/src/o_basic.c
+++ b/libgeda/src/o_basic.c
@@ -89,6 +89,10 @@ void o_recalc_single_object(TOPLEVEL *toplevel, OBJECT *o_current)
         o_box_recalc(toplevel, o_current);
         break;
 
+      case(OBJ_PATH):
+        o_path_recalc(toplevel, o_current);
+        break;
+
       case(OBJ_PICTURE):
         o_picture_recalc(toplevel, o_current);
         break;
@@ -281,6 +285,7 @@ void o_translate_world (TOPLEVEL *toplevel, gint dx, gint dy, OBJECT *object)
       case OBJ_PLACEHOLDER:
       case OBJ_COMPLEX: func = o_complex_translate_world; break;
       case OBJ_TEXT:    func = o_text_translate_world;    break;
+      case OBJ_PATH:    func = o_path_translate_world;    break;
       case OBJ_PIN:     func = o_pin_translate_world;     break;
       case OBJ_ARC:     func = o_arc_translate_world;     break;
       default:
@@ -320,6 +325,7 @@ void o_rotate_world (TOPLEVEL *toplevel, int world_centerx, int world_centery, i
       case OBJ_PLACEHOLDER:
       case OBJ_COMPLEX: func = o_complex_rotate_world;    break;
       case OBJ_TEXT:    func = o_text_rotate_world;       break;
+      case OBJ_PATH:    func = o_path_rotate_world;       break;
       case OBJ_PIN:     func = o_pin_rotate_world;        break;
       case OBJ_ARC:     func = o_arc_rotate_world;        break;
       default:
@@ -358,6 +364,7 @@ void o_mirror_world (TOPLEVEL *toplevel, int world_centerx, int world_centery, O
       case OBJ_PLACEHOLDER:
       case OBJ_COMPLEX: func = o_complex_mirror_world;    break;
       case OBJ_TEXT:    func = o_text_mirror_world;       break;
+      case OBJ_PATH:    func = o_path_mirror_world;       break;
       case OBJ_PIN:     func = o_pin_mirror_world;        break;
       case OBJ_ARC:     func = o_arc_mirror_world;        break;
       default:
@@ -420,6 +427,10 @@ gdouble o_shortest_distance(OBJECT *object, gint x, gint y)
     case(OBJ_HEAD):
       break;
 
+    case(OBJ_PATH):
+      shortest_distance = o_path_shortest_distance(object, x, y);
+      break;
+
     case(OBJ_PICTURE):
       shortest_distance = o_picture_shortest_distance(object->picture, x, y);
       break;
diff --git a/libgeda/src/o_complex_basic.c b/libgeda/src/o_complex_basic.c
index 8d02aee..284c5f1 100644
--- a/libgeda/src/o_complex_basic.c
+++ b/libgeda/src/o_complex_basic.c
@@ -68,6 +68,7 @@ int world_get_single_object_bounds(TOPLEVEL *toplevel, OBJECT *o_current,
       case(OBJ_BOX):
       case(OBJ_PICTURE):
       case(OBJ_CIRCLE):
+      case(OBJ_PATH):
       case(OBJ_PIN):
       case(OBJ_ARC):
       case(OBJ_COMPLEX):
@@ -934,6 +935,7 @@ void o_complex_set_color(OBJECT *prim_objs, int color)
       case(OBJ_BOX):
       case(OBJ_PICTURE):
       case(OBJ_CIRCLE):
+      case(OBJ_PATH):
       case(OBJ_PIN):
       case(OBJ_ARC):
         o_current->color = color;
@@ -970,6 +972,7 @@ void o_complex_set_color_single(OBJECT *o_current, int color)
     case(OBJ_BOX):
     case(OBJ_PICTURE):
     case(OBJ_CIRCLE):
+    case(OBJ_PATH):
     case(OBJ_PIN):
     case(OBJ_ARC):
     o_current->color = color;
@@ -1008,6 +1011,7 @@ void o_complex_set_color_save(OBJECT *complex, int color)
       case(OBJ_BOX):
       case(OBJ_PICTURE):
       case(OBJ_CIRCLE):
+      case(OBJ_PATH):
       case(OBJ_PIN):
       case(OBJ_ARC):
         o_current->saved_color = o_current->color;
@@ -1054,6 +1058,7 @@ void o_complex_unset_color(OBJECT *complex)
       case(OBJ_BOX):
       case(OBJ_PICTURE):
       case(OBJ_CIRCLE):
+      case(OBJ_PATH):
       case(OBJ_PIN):
       case(OBJ_ARC):
         o_current->color = o_current->saved_color;
@@ -1095,6 +1100,7 @@ void o_complex_unset_color_single(OBJECT *o_current)
     case(OBJ_BOX):
     case(OBJ_PICTURE):
     case(OBJ_CIRCLE):
+    case(OBJ_PATH):
     case(OBJ_PIN):
     case(OBJ_ARC):
     o_current->color = o_current->saved_color;
@@ -1135,6 +1141,7 @@ void o_complex_set_saved_color_only(OBJECT *complex, int color)
       case(OBJ_BOX):
       case(OBJ_PICTURE):
       case(OBJ_CIRCLE):
+      case(OBJ_PATH):
       case(OBJ_PIN):
       case(OBJ_ARC):
         o_current->saved_color = color;
diff --git a/libgeda/src/o_list.c b/libgeda/src/o_list.c
index 15d5e11..4eece4c 100644
--- a/libgeda/src/o_list.c
+++ b/libgeda/src/o_list.c
@@ -102,6 +102,10 @@ OBJECT *o_list_copy_to(TOPLEVEL *toplevel, OBJECT *list_head,
       }
       break;
 
+    case(OBJ_PATH):
+      end = (OBJECT *) o_path_copy(toplevel, end, selected);
+      break;
+
     case(OBJ_PIN):
       end = (OBJECT *) o_pin_copy(toplevel, end, selected);
       break;
diff --git a/libgeda/src/o_path_basic.c b/libgeda/src/o_path_basic.c
new file mode 100644
index 0000000..439d8b2
--- /dev/null
+++ b/libgeda/src/o_path_basic.c
@@ -0,0 +1,707 @@
+/* gEDA - GPL Electronic Design Automation
+ * libgeda - gEDA's library
+ * Copyright (C) 1998-2007 Ales Hvezda
+ * Copyright (C) 1998-2008 gEDA Contributors (see ChangeLog for details)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111 USA
+ */
+#include <config.h>
+
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include "libgeda_priv.h"
+
+#ifdef HAVE_LIBDMALLOC
+#include <dmalloc.h>
+#endif
+
+/*! Default setting for path draw function. */
+void (*path_draw_func)() = NULL;
+
+/*! \brief Create and add path OBJECT to list.
+ *  \par Function Description
+ *  This function creates a new object representing a path.
+ *  This object is added to the end of the list <B>object_list</B>
+ *  pointed object belongs to.
+ *  The path is described by its two ends - <B>x1</B>,<B>y1</B> and
+ *  <B>x2</B>,<B>y2</B>.
+ *  The <B>type</B> parameter must be equal to #OBJ_PATH.
+ *  The <B>color</B> parameter corresponds to the color the box
+ *  will be drawn with.
+ *
+ *  The #OBJECT structure is allocated with the
+ *  #s_basic_init_object() function. The structure describing
+ *  the path is allocated and initialized with the parameters given
+ *  to the function.
+ *
+ *  Both the path type and the filling type are set to default
+ *  values : solid path type with a width of 0, and no filling.
+ *  It can be changed after with the #o_set_path_options() and
+ *  #o_set_fill_options().
+ *
+ *  The object is added to the end of the list described by the
+ *  <B>object_list</B> parameter by the #s_basic_link_object().
+ *
+ *  \param [in]     toplevel     The TOPLEVEL object.
+ *  \param [in,out] object_list  OBJECT list to add path to.
+ *  \param [in]     type         Must be OBJ_PATH.
+ *  \param [in]     color        Circle path color.
+ *  \return A pointer to the new end of the object list.
+ */
+OBJECT *o_path_add (TOPLEVEL *toplevel, OBJECT *object_list,
+                    char type, int color, const char *path_string)
+{
+  OBJECT *new_node;
+
+  /* create the object */
+  new_node        = s_basic_new_object (type, "path");
+  new_node->color = color;
+
+  new_node->path  = s_path_parse (path_string);
+
+  /* path type and filling initialized to default */
+  o_set_line_options (toplevel, new_node,
+                      END_NONE, TYPE_SOLID, 0, -1, -1);
+  o_set_fill_options (toplevel, new_node,
+                      FILLING_HOLLOW, -1, -1, -1, -1, -1);
+
+  new_node->draw_func = path_draw_func;
+  new_node->sel_func = select_func;
+
+  /* compute bounding box */
+  o_path_recalc (toplevel, new_node);
+
+  object_list = (OBJECT *) s_basic_link_object (new_node, object_list);
+
+  return object_list;
+}
+
+
+/*! \brief Create a copy of a path.
+ *  \par Function Description
+ *  This function creates a verbatim copy of the
+ *  object pointed by <B>o_current</B> describing a path. The new object
+ *  is added at the end of the list following the <B>list_tail</B>
+ *  parameter.
+ *
+ *  \param [in]  toplevel  The TOPLEVEL object.
+ *  \param [out] list_tail  OBJECT list to copy to.
+ *  \param [in]  o_current  Line OBJECT to copy.
+ *  \return A new pointer to the end of the object list.
+ */
+OBJECT *o_path_copy (TOPLEVEL *toplevel, OBJECT *list_tail, OBJECT *o_current)
+{
+  OBJECT *new_obj;
+  char *path_string;
+  int color;
+
+  if (o_current->saved_color == -1) {
+    color = o_current->color;
+  } else {
+    color = o_current->saved_color;
+  }
+
+  /*
+   * A new path object is added a the end of the object list with
+   * #o_path_add ().
+   */
+
+  path_string = s_path_string_from_path (o_current->path);
+  new_obj = o_path_add (toplevel, list_tail, OBJ_PATH, color, path_string);
+  g_free (path_string);
+
+  /* copy the path type and filling options */
+  o_set_line_options (toplevel, new_obj, o_current->line_end,
+                      o_current->line_type, o_current->line_width,
+                      o_current->line_length, o_current->line_space);
+  o_set_fill_options (toplevel, new_obj,
+                      o_current->fill_type, o_current->fill_width,
+                      o_current->fill_pitch1, o_current->fill_angle1,
+                      o_current->fill_pitch2, o_current->fill_angle2);
+
+  /* calc the bounding box */
+  o_path_recalc (toplevel, o_current);
+
+  /* return the new tail of the object list */
+  return new_obj;
+}
+
+
+/*! \brief Create path OBJECT from character string.
+ *  \par Function Description
+ *  This function creates a path OBJECT from the character string
+ *  <B>*buf</B> the description of a box. The new box is added to the
+ *  list of objects of which <B>*object_list</B> is the last element
+ *  before the call.
+ *  The function returns a pointer on the new last element, that is
+ *  the added path object.
+ *
+ *  Depending on <B>*version</B>, the correct file format is considered.
+ *  Currently two file format revisions are supported :
+ *  <DL>
+ *    <DT>*</DT><DD>the file format used until 20010704 release.
+ *    <DT>*</DT><DD>the file format used for the releases after 20010704.
+ *  </DL>
+ *
+ *  \param [in]  toplevel       The TOPLEVEL object.
+ *  \param [out] object_list     OBJECT list to create path in.
+ *  \param [in]  first_line      Character string with path description.
+ *  \param [in]  tb              Text buffer containing the path string.
+ *  \param [in]  release_ver     libgeda release version number.
+ *  \param [in]  fileformat_ver  libgeda file format version number.
+ *  \return A pointer to the new path object.
+ */
+OBJECT *o_path_read (TOPLEVEL *toplevel, OBJECT *object_list,
+                     const char *first_line, TextBuffer *tb,
+                     unsigned int release_ver, unsigned int fileformat_ver)
+{
+  char type;
+  int color;
+  int line_width, line_space, line_length;
+  int line_end;
+  int line_type;
+  int fill_type, fill_width, angle1, pitch1, angle2, pitch2;
+  int num_lines = 0;
+  int i;
+  char *string;
+  GString *pathstr;
+
+  /*
+   * The current path format to describe a line is a space separated
+   * list of characters and numbers in plain ASCII on a single path.
+   * The meaning of each item is described in the file format documentation.
+   */
+  /* Allocate enough space */
+  sscanf (first_line, "%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+          &type, &color, &line_width, &line_end, &line_type,
+          &line_length, &line_space, &fill_type, &fill_width, &angle1,
+          &pitch1, &angle2, &pitch2, &num_lines);
+
+  /*
+   * Checks if the required color is valid.
+   */
+  if (color < 0 || color > MAX_COLORS) {
+    s_log_message (_("Found an invalid color [ %s ]\n"), first_line);
+    s_log_message (_("Setting color to WHITE\n"));
+    color = WHITE;
+  }
+
+  /*
+   * A path is internally described by its two ends. A new object is
+   * allocated, initialized and added to the list of objects. Its path
+   * type is set according to the values of the fields on the path.
+   */
+
+  pathstr = g_string_new ("");
+  for (i = 0; i < num_lines; i++) {
+    gchar *line;
+
+    line = s_textbuffer_next_line (tb);
+
+    if (line != NULL) {
+      pathstr = g_string_append (pathstr, line);
+    }
+  }
+
+  /* retrieve the character string from the GString */
+  string = g_string_free (pathstr, FALSE);
+  string = remove_last_nl (string);
+
+  /* create and add the path to the list */
+  object_list = o_path_add (toplevel, object_list, type, color, string);
+  g_free (string);
+
+  /* set its line options */
+  o_set_line_options (toplevel, object_list,
+                      line_end, line_type, line_width, line_length, line_space);
+  /* set its fill options */
+  o_set_fill_options (toplevel, object_list,
+                      fill_type, fill_width, pitch1, angle1, pitch2, angle2);
+
+  return object_list;
+}
+
+
+/*! \brief Create a character string representation of a path OBJECT.
+ *  \par Function Description
+ *  The function formats a string in the buffer <B>*buff</B> to describe
+ *  the box object <B>*object</B>.
+ *  It follows the post-20000704 release file format that handle the
+ *  path type and fill options - filling is irrelevant here.
+ *
+ *  \param [in] object  Line OBJECT to create string from.
+ *  \return A pointer to the path OBJECT character string.
+ *
+ *  \note
+ *  Caller must g_free returned character string.
+ *
+ */
+char *o_path_save (OBJECT *object)
+{
+  int color;
+  int line_width, line_space, line_length;
+  char *buf;
+  int num_lines;
+  OBJECT_END line_end;
+  OBJECT_TYPE line_type;
+  OBJECT_FILLING fill_type;
+  int fill_width, angle1, pitch1, angle2, pitch2;
+  char *path_string;
+
+  /* description of the line type */
+  line_width  = object->line_width;
+  line_end    = object->line_end;
+  line_type   = object->line_type;
+  line_length = object->line_length;
+  line_space  = object->line_space;
+
+  /* filling parameters */
+  fill_type    = object->fill_type;
+  fill_width   = object->fill_width;
+  angle1       = object->fill_angle1;
+  pitch1       = object->fill_pitch1;
+  angle2       = object->fill_angle2;
+  pitch2       = object->fill_pitch2;
+
+  /* Use the right color */
+  if (object->saved_color == -1) {
+    color = object->color;
+  } else {
+    color = object->saved_color;
+  }
+
+  path_string = s_path_string_from_path (object->path);
+  num_lines = o_text_num_lines (path_string);
+  buf = g_strdup_printf ("%c %d %d %d %d %d %d %d %d %d %d %d %d %d\n%s",
+                         object->type, color, line_width, line_end,
+                         line_type, line_length, line_space, fill_type,
+                         fill_width, angle1, pitch1, angle2, pitch2,
+                         num_lines, path_string);
+  g_free (path_string);
+
+  return buf;
+}
+
+
+/*! \brief Modify controol point location
+ *
+ *  \par Function Description
+ *  This function modifies a control point location of the path object
+ *  *object. The control point being modified is selected according to
+ *  the whichone parameter.
+ *
+ *  The new position is given by <B>x</B> and <B>y</B>.
+ *
+ *  \param [in]     toplevel  The TOPLEVEL object.
+ *  \param [in,out] object    The path OBJECT
+ *  \param [in]     x         New x coordinate for the control point
+ *  \param [in]     y         New y coordinate for the control point
+ *  \param [in]     whichone  Which control point is being modified
+ */
+void o_path_modify (TOPLEVEL *toplevel, OBJECT *object,
+                    int x, int y, int whichone)
+{
+  int i;
+  int grip_no = 0;
+  PATH_SECTION *section;
+
+  for (i = 0; i <  object->path->num_sections; i++) {
+    section = &object->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* Two control point grips */
+      if (whichone == grip_no++) {
+        section->x1 = x;
+        section->y1 = y;
+      }
+      if (whichone == grip_no++) {
+        section->x2 = x;
+        section->y2 = y;
+      }
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      /* Destination point grip */
+      if (whichone == grip_no++) {
+        section->x3 = x;
+        section->y3 = y;
+      }
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+
+  /* Update bounding box */
+  o_path_recalc (toplevel, object);
+}
+
+
+/*! \brief Translate a path position in WORLD coordinates by a delta.
+ *  \par Function Description
+ *  This function applies a translation of (<B>x1</B>,<B>y1</B>) to the path
+ *  described by <B>*object</B>. <B>x1</B> and <B>y1</B> are in world unit.
+ *
+ *  \param [in]     toplevel  The TOPLEVEL object.
+ *  \param [in]     dx         x distance to move.
+ *  \param [in]     dy         y distance to move.
+ *  \param [in,out] object     Line OBJECT to translate.
+ */
+void o_path_translate_world (TOPLEVEL *toplevel,
+                             int dx, int dy, OBJECT *object)
+{
+  PATH_SECTION *section;
+  int i;
+
+  for (i = 0; i < object->path->num_sections; i++) {
+    section = &object->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      section->x1 += dx;
+      section->y1 += dy;
+      section->x2 += dx;
+      section->y2 += dy;
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      section->x3 += dx;
+      section->y3 += dy;
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+
+  /* Update bounding box */
+  o_path_recalc (toplevel, object);
+}
+
+
+/*! \brief Rotate Line OBJECT using WORLD coordinates.
+ *  \par Function Description
+ *  This function rotates the path described by
+ *  <B>*object</B> around the (<B>world_centerx</B>,<B>world_centery</B>)
+ *  point by <B>angle</B> degrees.
+ *  The center of rotation is in world units.
+ *
+ *  \param [in]      toplevel      The TOPLEVEL object.
+ *  \param [in]      world_centerx  Rotation center x coordinate in WORLD units.
+ *  \param [in]      world_centery  Rotation center y coordinate in WORLD units.
+ *  \param [in]      angle          Rotation angle in degrees (See note below).
+ *  \param [in,out]  object         Line OBJECT to rotate.
+ */
+void o_path_rotate_world (TOPLEVEL *toplevel,
+                          int world_centerx, int world_centery, int angle,
+                          OBJECT *object)
+{
+  PATH_SECTION *section;
+  int i;
+
+  for (i = 0; i < object->path->num_sections; i++) {
+    section = &object->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* Two control point grips */
+      section->x1 -= world_centerx; section->y1 -= world_centery;
+      section->x2 -= world_centerx; section->y2 -= world_centery;
+      rotate_point_90 (section->x1, section->y1, angle, &section->x1, &section->y1);
+      rotate_point_90 (section->x2, section->y2, angle, &section->x2, &section->y2);
+      section->x1 += world_centerx; section->y1 += world_centery;
+      section->x2 += world_centerx; section->y2 += world_centery;
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      /* Destination point grip */
+      section->x3 -= world_centerx; section->y3 -= world_centery;
+      rotate_point_90 (section->x3, section->y3, angle, &section->x3, &section->y3);
+      section->x3 += world_centerx; section->y3 += world_centery;
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+  o_path_recalc (toplevel, object);
+}
+
+
+/*! \brief Mirror a path using WORLD coordinates.
+ *  \par Function Description
+ *  This function mirrors the path from the point
+ *  (<B>world_centerx</B>,<B>world_centery</B>) in world unit.
+ *
+ *  \param [in]     toplevel      The TOPLEVEL object.
+ *  \param [in]     world_centerx  Origin x coordinate in WORLD units.
+ *  \param [in]     world_centery  Origin y coordinate in WORLD units.
+ *  \param [in,out] object         Line OBJECT to mirror.
+ */
+void o_path_mirror_world (TOPLEVEL *toplevel, int world_centerx,
+                          int world_centery, OBJECT *object)
+{
+  PATH_SECTION *section;
+  int i;
+
+  for (i = 0; i < object->path->num_sections; i++) {
+    section = &object->path->sections[i];
+
+    switch (section->code) {
+    case PATH_CURVETO:
+      /* Two control point grips */
+      section->x1 = 2 * world_centerx - section->x1;
+      section->x2 = 2 * world_centerx - section->x2;
+      /* Fall through */
+    case PATH_MOVETO:
+    case PATH_MOVETO_OPEN:
+    case PATH_LINETO:
+      /* Destination point grip */
+      section->x3 = 2 * world_centerx - section->x3;
+      break;
+    case PATH_END:
+      break;
+    }
+  }
+
+  o_path_recalc (toplevel, object);
+}
+
+
+/*! \brief Recalculate path coordinates in SCREEN units.
+ *  \par Function Description
+ *  This function recalculate the bounding box of the <B>o_current</B>
+ *
+ *  \param [in] toplevel      The TOPLEVEL object.
+ *  \param [in,out] o_current  Line OBJECT to be recalculated.
+ */
+void o_path_recalc (TOPLEVEL *toplevel, OBJECT *o_current)
+{
+  int left, right, top, bottom;
+
+  if (o_current->path == NULL) {
+    return;
+  }
+
+  /* Update the bounding box */
+  world_get_path_bounds (toplevel, o_current, &left, &top, &right, &bottom);
+  o_current->w_left   = left;
+  o_current->w_top    = top;
+  o_current->w_right  = right;
+  o_current->w_bottom = bottom;
+  o_current->w_bounds_valid = TRUE;
+}
+
+
+/*! \brief Get path bounding rectangle in WORLD coordinates.
+ *  \par Function Description
+ *  This function sets the <B>left</B>, <B>top</B>, <B>right</B> and
+ *  <B>bottom</B> parameters to the boundings of the path object described
+ *  in <B>*path</B> in world units.
+ *
+ *  \note Bounding box for bezier curves is loose because we just consider
+ *        the convex hull of the curve control and end-points.
+ *
+ *  \param [in]  toplevel  The TOPLEVEL object.
+ *  \param [in]  OBJECT     Line OBJECT to read coordinates from.
+ *  \param [out] left       Left path coordinate in WORLD units.
+ *  \param [out] top        Top path coordinate in WORLD units.
+ *  \param [out] right      Right path coordinate in WORLD units.
+ *  \param [out] bottom     Bottom path coordinate in WORLD units.
+ */
+void world_get_path_bounds (TOPLEVEL *toplevel, OBJECT *object,
+                            int *left, int *top, int *right, int *bottom)
+{
+  PATH_SECTION *section;
+  int halfwidth;
+  int i;
+  int found_bound = FALSE;
+
+  /* Find the bounds of the path region */
+  for (i = 0; i < object->path->num_sections; i++) {
+    section = &object->path->sections[i];
+    switch (section->code) {
+      case PATH_CURVETO:
+        /* Bezier curves with this construction of control points will lie
+         * within the convex hull of the control and curve end points */
+        *left   = (found_bound) ? MIN (*left,   section->x1) : section->x1;
+        *top    = (found_bound) ? MIN (*top,    section->y1) : section->y1;
+        *right  = (found_bound) ? MAX (*right,  section->x1) : section->x1;
+        *bottom = (found_bound) ? MAX (*bottom, section->y1) : section->y1;
+        found_bound = TRUE;
+        *left   = MIN (*left,   section->x2);
+        *top    = MIN (*top,    section->y2);
+        *right  = MAX (*right,  section->x2);
+        *bottom = MAX (*bottom, section->y2);
+        /* Fall through */
+      case PATH_MOVETO:
+      case PATH_MOVETO_OPEN:
+      case PATH_LINETO:
+        *left   = (found_bound) ? MIN (*left,   section->x3) : section->x3;
+        *top    = (found_bound) ? MIN (*top,    section->y3) : section->y3;
+        *right  = (found_bound) ? MAX (*right,  section->x3) : section->x3;
+        *bottom = (found_bound) ? MAX (*bottom, section->y3) : section->y3;
+        found_bound = TRUE;
+        break;
+      case PATH_END:
+        break;
+    }
+  }
+
+  if (found_bound) {
+    /* This isn't strictly correct, but a 1st order approximation */
+    halfwidth = object->line_width / 2;
+    *left   -= halfwidth;
+    *top    -= halfwidth;
+    *right  += halfwidth;
+    *bottom += halfwidth;
+  }
+}
+
+
+/*! \brief Print path to Postscript document.
+ *  \par Function Description
+ *  This function prints the path described by the <B>o_current</B>
+ *  parameter to a Postscript document.
+ *  The Postscript document is described by the <B>fp</B> file pointer.
+ *
+ *  Parameters of the path are extracted from object pointed by
+ *  <B>o_current</B>.
+ *
+ *  \param [in] toplevel  The TOPLEVEL object.
+ *  \param [in] fp         FILE pointer to Postscript document.
+ *  \param [in] o_current  Line OBJECT to write to document.
+ *  \param [in] origin_x   Page x coordinate to place path OBJECT.
+ *  \param [in] origin_y   Page y coordinate to place path OBJECT.
+ */
+void o_path_print (TOPLEVEL *toplevel, FILE *fp, OBJECT *o_current,
+                   int origin_x, int origin_y)
+{
+  PATH_SECTION *section;
+  GString *path_string;
+  int line_width;
+  int i;
+
+  line_width = o_current->line_width;
+  if (line_width <=2) {
+    if (toplevel->line_style == THICK) {
+      line_width = LINE_WIDTH;
+    } else {
+      line_width = 2;
+    }
+  }
+
+  path_string = g_string_new ("");
+
+  f_print_set_color (fp, o_current->color);
+  f_print_set_line_width (fp, line_width);
+
+  for (i = 0; i < o_current->path->num_sections; i++) {
+    section = &o_current->path->sections[i];
+
+    if (i > 0)
+      fprintf (fp, " ");
+
+    switch (section->code) {
+      case PATH_MOVETO:
+        fprintf (fp, "closepath ");
+        /* Fall through */
+      case PATH_MOVETO_OPEN:
+        fprintf (fp, "%i %i moveto",
+                     section->x3 - origin_x, section->y3 - origin_y);
+        break;
+      case PATH_CURVETO:
+        fprintf (fp, "%i %i %i %i %i %i curveto",
+                     section->x1 - origin_x, section->y1 - origin_y,
+                     section->x2 - origin_x, section->y2 - origin_y,
+                     section->x3 - origin_x, section->y3 - origin_y);
+        break;
+      case PATH_LINETO:
+        fprintf (fp, "%i %i lineto",
+                     section->x3 - origin_x, section->y3 - origin_y);
+        break;
+      case PATH_END:
+        fprintf (fp, "closepath ");
+        break;
+    }
+  }
+
+  if (o_current->fill_type == FILLING_HOLLOW) {
+    fprintf (fp, "stroke\n");
+  } else {
+    fprintf (fp, "gsave fill grestore stroke\n");
+  }
+}
+
+
+/*! \brief Calculates the distance between the given point and the closest
+ *  point on the given path segment.
+ *
+ *  \todo Support for bezier path segments.
+ *
+ *  \param [in] object The path OBJECT
+ *  \param [in] x The x coordinate of the given point.
+ *  \param [in] y The y coordinate of the given point.
+ *  \return The shortest distance from the object to the point.  With an
+ *  invalid parameter, this function returns G_MAXDOUBLE.
+ */
+gdouble o_path_shortest_distance (OBJECT *object, gint x, gint y)
+{
+  PATH_SECTION *section;
+  LINE line;
+  gdouble shortest = G_MAXDOUBLE;
+  int last_x = 0, last_y = 0;
+  int i;
+
+  for (i = 0; i < object->path->num_sections; i++) {
+    section = &object->path->sections[i];
+    switch (section->code) {
+
+      case PATH_CURVETO:
+        /* TODO: Shortest distance to a besier section of the path.
+         *       For now, pretend it is a straight line. */
+        /* Fall through */
+      case PATH_LINETO:
+        line.x[0] = last_x;
+        line.y[0] = last_y;
+        line.x[1] = last_x = section->x3;
+        line.y[1] = last_y = section->y3;
+        shortest = MIN (shortest, o_line_shortest_distance (&line, x, y));
+        break;
+
+      case PATH_MOVETO:
+      case PATH_MOVETO_OPEN:
+        last_x = section->x3;
+        last_y = section->y3;
+        break;
+
+      case PATH_END:
+        /* Need to consider the line back to the first point in the path */
+        line.x[0] = last_x;
+        line.y[0] = last_y;
+        line.x[1] = last_x = object->path->sections[0].x3;
+        line.y[1] = last_y = object->path->sections[0].y3;
+        shortest = MIN (shortest, o_line_shortest_distance (&line, x, y));
+        break;
+    }
+  }
+
+  return shortest;
+}
+
diff --git a/libgeda/src/s_basic.c b/libgeda/src/s_basic.c
index 8ce1fd5..b498b8f 100644
--- a/libgeda/src/s_basic.c
+++ b/libgeda/src/s_basic.c
@@ -131,6 +131,7 @@ OBJECT *s_basic_init_object(OBJECT *new_node, int type, char const *name)
 
   /* Setup line/circle structs */
   new_node->line = NULL;
+  new_node->path = NULL;
   new_node->circle = NULL;
   new_node->arc = NULL;
   new_node->box = NULL;
@@ -335,6 +336,11 @@ s_delete_object(TOPLEVEL *toplevel, OBJECT *o_current)
     }
     o_current->line = NULL;
 
+    if (o_current->path) {
+      g_free(o_current->path);
+    }
+    o_current->path = NULL;
+
     /*	printf("sdeleting circle\n");*/
     g_free(o_current->circle);
     o_current->circle = NULL;
diff --git a/libgeda/src/s_path.c b/libgeda/src/s_path.c
new file mode 100644
index 0000000..36065b0
--- /dev/null
+++ b/libgeda/src/s_path.c
@@ -0,0 +1,698 @@
+/* gEDA - GPL Electronic Design Automation
+ * libgeda - gEDA's library
+ * Copyright (C) 1998-2007 gEDA Contributors (see ChangeLog for details)
+ *
+ * Code originally from librsvg 2.22.2 (LGPL) Copyright (C) 2000 Eazel, Inc.
+ *
+ *   Author: Raph Levien <raph@xxxxxxxxxxxxx>
+ *     rsvg-path.c:       Parse SVG path element data into bezier path.
+ *     rsvg-bpath-util.c: Data structure and convenience functions for
+ *                        creating bezier paths.
+ *
+ *  Adapted for gEDA by Peter Clifton <pcjc2@xxxxxxxxx>
+ *
+ *  THIS FILE IS LGPL LICENSED, gEDA AS A WHOLE IS GPL LICENSED
+ *
+ * This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library 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
+ *  Library General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Library General Public
+ *  License along with this program; if not, write to the
+ *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ *  Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib/gmem.h>
+#include <glib/gmessages.h>
+#include <glib/gtypes.h>
+
+#include "libgeda_priv.h"
+
+
+PATH *s_path_new (void)
+{
+  PATH *path;
+
+  path = g_new (PATH, 1);
+  path->num_sections = 0;
+  path->num_sections_max = 16;
+  path->sections = g_new (PATH_SECTION, path->num_sections_max);
+
+  return path;
+}
+
+
+PATH *s_path_new_from (PATH_SECTION *sections)
+{
+  PATH *path;
+  int i;
+
+  g_return_val_if_fail (sections != NULL, NULL);
+
+  for (i = 0; sections[i].code != PATH_END; i++);
+  if (i <= 0)
+    return s_path_new ();
+
+  path = g_new (PATH, 1);
+
+  path->num_sections = i;
+  path->num_sections_max = i;
+  path->sections = g_new (PATH_SECTION, i);
+
+  memcpy (path->sections, sections, i * sizeof (PATH_SECTION));
+  return path;
+}
+
+
+void s_path_free (PATH * path)
+{
+  g_return_if_fail (path != NULL);
+
+  g_free (path->sections);
+  g_free (path);
+}
+
+
+void s_path_moveto (PATH *path, double x, double y)
+{
+  PATH_SECTION *sections;
+  int num_sections;
+
+  g_return_if_fail (path != NULL);
+
+  /* if the last command was a moveto then change that last moveto instead of
+     creating a new one */
+  sections = path->sections;
+  num_sections = path->num_sections;
+
+  if (num_sections > 0)
+    if (sections[num_sections - 1].code == PATH_MOVETO_OPEN) {
+      sections[num_sections - 1].x3 = x;
+      sections[num_sections - 1].y3 = y;
+      return;
+    }
+
+  num_sections = path->num_sections++;
+
+  if (num_sections == path->num_sections_max)
+    path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
+  sections = path->sections;
+  sections[num_sections].code = PATH_MOVETO_OPEN;
+  sections[num_sections].x3 = x;
+  sections[num_sections].y3 = y;
+}
+
+
+void s_path_lineto (PATH *path, double x, double y)
+{
+  PATH_SECTION *sections;
+  int num_sections;
+
+  g_return_if_fail (path != NULL);
+
+  num_sections = path->num_sections++;
+
+  if (num_sections == path->num_sections_max)
+    path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
+  sections = path->sections;
+  sections[num_sections].code = PATH_LINETO;
+  sections[num_sections].x3 = x;
+  sections[num_sections].y3 = y;
+}
+
+
+void s_path_curveto (PATH *path, double x1, double y1,
+                     double x2, double y2, double x3, double y3)
+{
+  PATH_SECTION *sections;
+  int num_sections;
+
+  g_return_if_fail (path != NULL);
+
+  num_sections = path->num_sections++;
+
+  if (num_sections == path->num_sections_max)
+    path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
+  sections = path->sections;
+  sections[num_sections].code = PATH_CURVETO;
+  sections[num_sections].x1 = x1;
+  sections[num_sections].y1 = y1;
+  sections[num_sections].x2 = x2;
+  sections[num_sections].y2 = y2;
+  sections[num_sections].x3 = x3;
+  sections[num_sections].y3 = y3;
+}
+
+
+void s_path_art_finish (PATH * path)
+{
+  int num_sections;
+
+  g_return_if_fail (path != NULL);
+
+  num_sections = path->num_sections++;
+
+  if (num_sections == path->num_sections_max)
+    path->sections = g_realloc (path->sections, (path->num_sections_max <<= 1) * sizeof (PATH_SECTION));
+  path->sections[num_sections].code = PATH_END;
+}
+
+
+/* This module parses an SVG style path element into a PATH.
+
+  At present, there is no support for <marker> or any other contextual
+  information from the SVG file. The API will need to change rather
+  significantly to support these.
+
+  Reference: SVG working draft 3 March 2000, section 8.
+*/
+
+typedef struct _RSVGParsePathCtx RSVGParsePathCtx;
+
+struct _RSVGParsePathCtx {
+  PATH *path;
+  double cpx, cpy;    /* current point */
+  double rpx, rpy;    /* reflection point (for 's' and 't' commands) */
+  double mpx, mpy;    /* Last moved to point (for path closures) */
+  char cmd;           /* current command (lowercase) */
+  int param;          /* parameter number */
+  gboolean rel;       /* true if relative coords */
+  double params[7];   /* parameters that have been parsed */
+};
+
+
+static void s_path_arc_segment (RSVGParsePathCtx * ctx,
+                                double xc, double yc, double th0, double th1,
+                                double rx, double ry, double x_axis_rotation)
+{
+  double sin_th, cos_th;
+  double a00, a01, a10, a11;
+  double x1, y1, x2, y2, x3, y3;
+  double t;
+  double th_half;
+
+  sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+  cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+  /* inverse transform compared with s_path_arc */
+  a00 = cos_th * rx;
+  a01 = -sin_th * ry;
+  a10 = sin_th * rx;
+  a11 = cos_th * ry;
+
+  th_half = 0.5 * (th1 - th0);
+  t = (8.0 / 3.0) * sin (th_half * 0.5) * sin (th_half * 0.5) / sin (th_half);
+  x1 = xc + cos (th0) - t * sin (th0);
+  y1 = yc + sin (th0) + t * cos (th0);
+  x3 = xc + cos (th1);
+  y3 = yc + sin (th1);
+  x2 = x3 + t * sin (th1);
+  y2 = y3 - t * cos (th1);
+  s_path_curveto (ctx->path,
+              a00 * x1 + a01 * y1, a10 * x1 + a11 * y1,
+              a00 * x2 + a01 * y2, a10 * x2 + a11 * y2,
+              a00 * x3 + a01 * y3, a10 * x3 + a11 * y3);
+}
+
+
+/*
+ * s_path_arc: Add an arc to the path context.
+ * @ctx: Path context.
+ * @rx: Radius in x direction (before rotation).
+ * @ry: Radius in y direction (before rotation).
+ * @x_axis_rotation: Rotation angle for axes.
+ * @large_arc_flag: 0 for arc length <= 180, 1 for arc >= 180.
+ * @sweep: 0 for "negative angle", 1 for "positive angle".
+ * @x: New x coordinate.
+ * @y: New y coordinate.
+ *
+ */
+static void s_path_arc (RSVGParsePathCtx * ctx,
+                        double rx, double ry, double x_axis_rotation,
+                        int large_arc_flag, int sweep_flag, double x, double y)
+{
+  double sin_th, cos_th;
+  double a00, a01, a10, a11;
+  double x0, y0, x1, y1, xc, yc;
+  double d, sfactor, sfactor_sq;
+  double th0, th1, th_arc;
+  int i, n_segs;
+
+  /* Check that neither radius is zero, since its isn't either
+     geometrically or mathematically meaningful and will
+     cause divide by zero and subsequent NaNs.  We should
+     really do some ranged check ie -0.001 < x < 000.1 rather
+     can just a straight check again zero.
+   */
+  if ((rx == 0.0) || (ry == 0.0))
+    return;
+
+  sin_th = sin (x_axis_rotation * (M_PI / 180.0));
+  cos_th = cos (x_axis_rotation * (M_PI / 180.0));
+  a00 = cos_th / rx;
+  a01 = sin_th / rx;
+  a10 = -sin_th / ry;
+  a11 = cos_th / ry;
+  x0 = a00 * ctx->cpx + a01 * ctx->cpy;
+  y0 = a10 * ctx->cpx + a11 * ctx->cpy;
+  x1 = a00 * x + a01 * y;
+  y1 = a10 * x + a11 * y;
+  /* (x0, y0) is current point in transformed coordinate space.
+     (x1, y1) is new point in transformed coordinate space.
+
+     The arc fits a unit-radius circle in this space.
+   */
+  d = (x1 - x0) * (x1 - x0) + (y1 - y0) * (y1 - y0);
+  sfactor_sq = 1.0 / d - 0.25;
+  if (sfactor_sq < 0)
+    sfactor_sq = 0;
+  sfactor = sqrt (sfactor_sq);
+  if (sweep_flag == large_arc_flag)
+    sfactor = -sfactor;
+  xc = 0.5 * (x0 + x1) - sfactor * (y1 - y0);
+  yc = 0.5 * (y0 + y1) + sfactor * (x1 - x0);
+  /* (xc, yc) is center of the circle. */
+
+  th0 = atan2 (y0 - yc, x0 - xc);
+  th1 = atan2 (y1 - yc, x1 - xc);
+
+  th_arc = th1 - th0;
+  if (th_arc < 0 && sweep_flag)
+    th_arc += 2 * M_PI;
+  else if (th_arc > 0 && !sweep_flag)
+    th_arc -= 2 * M_PI;
+
+  n_segs = ceil (fabs (th_arc / (M_PI * 0.5 + 0.001)));
+
+  for (i = 0; i < n_segs; i++)
+    s_path_arc_segment (ctx, xc, yc,
+                 th0 + i * th_arc / n_segs,
+                 th0 + (i + 1) * th_arc / n_segs, rx, ry, x_axis_rotation);
+
+  ctx->cpx = x;
+  ctx->cpy = y;
+}
+
+
+/* supply defaults for missing parameters, assuming relative coordinates
+   are to be interpreted as x,y */
+static void s_path_parse_default_xy (RSVGParsePathCtx * ctx, int n_params)
+{
+  int i;
+
+  if (ctx->rel) {
+    for (i = ctx->param; i < n_params; i++) {
+      if (i > 2)
+        ctx->params[i] = ctx->params[i - 2];
+      else if (i == 1)
+        ctx->params[i] = ctx->cpy;
+      else if (i == 0)
+        /* we shouldn't get here (usually ctx->param > 0 as
+           precondition) */
+        ctx->params[i] = ctx->cpx;
+    }
+  } else {
+    for (i = ctx->param; i < n_params; i++)
+      ctx->params[i] = 0.0;
+  }
+}
+
+
+static void s_path_parse_do_cmd (RSVGParsePathCtx * ctx, gboolean final)
+{
+  double x1, y1, x2, y2, x3, y3;
+
+  switch (ctx->cmd) {
+  case 'm':
+    /* moveto */
+    if (ctx->param == 2 || final) {
+      s_path_parse_default_xy (ctx, 2);
+      s_path_moveto (ctx->path, ctx->params[0], ctx->params[1]);
+      ctx->mpx = ctx->cpx = ctx->rpx = ctx->params[0];
+      ctx->mpy = ctx->cpy = ctx->rpy = ctx->params[1];
+      ctx->param = 0;
+      ctx->cmd = 'l'; /* implicit linetos after a moveto */
+    }
+    break;
+  case 'l':
+    /* lineto */
+    if (ctx->param == 2 || final) {
+      s_path_parse_default_xy (ctx, 2);
+      s_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
+      ctx->cpx = ctx->rpx = ctx->params[0];
+      ctx->cpy = ctx->rpy = ctx->params[1];
+      ctx->param = 0;
+    }
+    break;
+  case 'c':
+    /* curveto */
+    if (ctx->param == 6 || final) {
+      s_path_parse_default_xy (ctx, 6);
+      x1 = ctx->params[0];
+      y1 = ctx->params[1];
+      x2 = ctx->params[2];
+      y2 = ctx->params[3];
+      x3 = ctx->params[4];
+      y3 = ctx->params[5];
+      s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
+      ctx->rpx = x2;
+      ctx->rpy = y2;
+      ctx->cpx = x3;
+      ctx->cpy = y3;
+      ctx->param = 0;
+    }
+    break;
+  case 's':
+    /* smooth curveto */
+    if (ctx->param == 4 || final) {
+      s_path_parse_default_xy (ctx, 4);
+      x1 = 2 * ctx->cpx - ctx->rpx;
+      y1 = 2 * ctx->cpy - ctx->rpy;
+      x2 = ctx->params[0];
+      y2 = ctx->params[1];
+      x3 = ctx->params[2];
+      y3 = ctx->params[3];
+      s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
+      ctx->rpx = x2;
+      ctx->rpy = y2;
+      ctx->cpx = x3;
+      ctx->cpy = y3;
+      ctx->param = 0;
+    }
+    break;
+  case 'h':
+    /* horizontal lineto */
+    if (ctx->param == 1) {
+      s_path_lineto (ctx->path, ctx->params[0], ctx->cpy);
+      ctx->cpx = ctx->rpx = ctx->params[0];
+      ctx->param = 0;
+    }
+    break;
+  case 'v':
+    /* vertical lineto */
+    if (ctx->param == 1) {
+      s_path_lineto (ctx->path, ctx->cpx, ctx->params[0]);
+      ctx->cpy = ctx->rpy = ctx->params[0];
+      ctx->param = 0;
+    }
+    break;
+  case 'q':
+    /* quadratic bezier curveto */
+
+    /* non-normative reference:
+       http://www.icce.rug.nl/erikjan/bluefuzz/beziers/beziers/beziers.html
+     */
+    if (ctx->param == 4 || final) {
+      s_path_parse_default_xy (ctx, 4);
+      /* raise quadratic bezier to cubic */
+      x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+      y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+      x3 = ctx->params[2];
+      y3 = ctx->params[3];
+      x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+      y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+      s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
+      ctx->rpx = ctx->params[0];
+      ctx->rpy = ctx->params[1];
+      ctx->cpx = x3;
+      ctx->cpy = y3;
+      ctx->param = 0;
+    }
+    break;
+  case 't':
+    /* Truetype quadratic bezier curveto */
+    if (ctx->param == 2 || final) {
+      double xc, yc;    /* quadratic control point */
+
+      xc = 2 * ctx->cpx - ctx->rpx;
+      yc = 2 * ctx->cpy - ctx->rpy;
+      /* generate a quadratic bezier with control point = xc, yc */
+      x1 = (ctx->cpx + 2 * xc) * (1.0 / 3.0);
+      y1 = (ctx->cpy + 2 * yc) * (1.0 / 3.0);
+      x3 = ctx->params[0];
+      y3 = ctx->params[1];
+      x2 = (x3 + 2 * xc) * (1.0 / 3.0);
+      y2 = (y3 + 2 * yc) * (1.0 / 3.0);
+      s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
+      ctx->rpx = xc;
+      ctx->rpy = yc;
+      ctx->cpx = x3;
+      ctx->cpy = y3;
+      ctx->param = 0;
+    } else if (final) {
+      if (ctx->param > 2) {
+        s_path_parse_default_xy (ctx, 4);
+        /* raise quadratic bezier to cubic */
+        x1 = (ctx->cpx + 2 * ctx->params[0]) * (1.0 / 3.0);
+        y1 = (ctx->cpy + 2 * ctx->params[1]) * (1.0 / 3.0);
+        x3 = ctx->params[2];
+        y3 = ctx->params[3];
+        x2 = (x3 + 2 * ctx->params[0]) * (1.0 / 3.0);
+        y2 = (y3 + 2 * ctx->params[1]) * (1.0 / 3.0);
+        s_path_curveto (ctx->path, x1, y1, x2, y2, x3, y3);
+        ctx->rpx = ctx->params[0];
+        ctx->rpy = ctx->params[1];
+        ctx->cpx = x3;
+        ctx->cpy = y3;
+      } else {
+        s_path_parse_default_xy (ctx, 2);
+        s_path_lineto (ctx->path, ctx->params[0], ctx->params[1]);
+        ctx->cpx = ctx->rpx = ctx->params[0];
+        ctx->cpy = ctx->rpy = ctx->params[1];
+      }
+      ctx->param = 0;
+    }
+    break;
+  case 'a':
+    if (ctx->param == 7 || final) {
+      s_path_arc (ctx,
+               ctx->params[0], ctx->params[1], ctx->params[2],
+               ctx->params[3], ctx->params[4], ctx->params[5], ctx->params[6]);
+      ctx->param = 0;
+    }
+    break;
+  default:
+    ctx->param = 0;
+  }
+}
+
+
+static void s_path_parse_data (RSVGParsePathCtx * ctx, const char *data)
+{
+  int i = 0;
+  double val = 0;
+  char c = 0;
+  gboolean in_num = FALSE;
+  gboolean in_frac = FALSE;
+  gboolean in_exp = FALSE;
+  gboolean exp_wait_sign = FALSE;
+  int sign = 0;
+  int exp = 0;
+  int exp_sign = 0;
+  double frac = 0.0;
+
+  in_num = FALSE;
+  for (i = 0;; i++) {
+    c = data[i];
+    if (c >= '0' && c <= '9') {
+      /* digit */
+      if (in_num) {
+        if (in_exp) {
+          exp = (exp * 10) + c - '0';
+          exp_wait_sign = FALSE;
+        } else if (in_frac)
+          val += (frac *= 0.1) * (c - '0');
+        else
+          val = (val * 10) + c - '0';
+      } else {
+        in_num = TRUE;
+        in_frac = FALSE;
+        in_exp = FALSE;
+        exp = 0;
+        exp_sign = 1;
+        exp_wait_sign = FALSE;
+        val = c - '0';
+        sign = 1;
+      }
+    } else if (c == '.') {
+      if (!in_num) {
+        in_num = TRUE;
+        val = 0;
+      }
+      in_frac = TRUE;
+      frac = 1;
+    } else if ((c == 'E' || c == 'e') && in_num) {
+      in_exp = TRUE;
+      exp_wait_sign = TRUE;
+      exp = 0;
+      exp_sign = 1;
+    } else if ((c == '+' || c == '-') && in_exp) {
+      exp_sign = c == '+' ? 1 : -1;
+    } else if (in_num) {
+      /* end of number */
+
+      val *= sign * pow (10, exp_sign * exp);
+      if (ctx->rel) {
+        /* Handle relative coordinates. This switch statement attempts
+           to determine _what_ the coords are relative to. This is
+           underspecified in the 12 Apr working draft. */
+        switch (ctx->cmd) {
+        case 'l':
+        case 'm':
+        case 'c':
+        case 's':
+        case 'q':
+        case 't':
+#ifndef RSVGV_RELATIVE
+          /* rule: even-numbered params are x-relative, odd-numbered
+             are y-relative */
+          if ((ctx->param & 1) == 0)
+            val += ctx->cpx;
+          else if ((ctx->param & 1) == 1)
+            val += ctx->cpy;
+          break;
+#else
+          /* rule: even-numbered params are x-relative, odd-numbered
+             are y-relative */
+          if (ctx->param == 0 || (ctx->param % 2 == 0))
+            val += ctx->cpx;
+          else
+            val += ctx->cpy;
+          break;
+#endif
+        case 'a':
+          /* rule: sixth and seventh are x and y, rest are not
+             relative */
+          if (ctx->param == 5)
+            val += ctx->cpx;
+          else if (ctx->param == 6)
+            val += ctx->cpy;
+          break;
+        case 'h':
+          /* rule: x-relative */
+          val += ctx->cpx;
+          break;
+        case 'v':
+          /* rule: y-relative */
+          val += ctx->cpy;
+          break;
+        }
+      }
+      ctx->params[ctx->param++] = val;
+      s_path_parse_do_cmd (ctx, FALSE);
+
+      in_num = FALSE;
+    }
+
+    if (c == '\0')
+      break;
+    else if ((c == '+' || c == '-') && !exp_wait_sign) {
+      sign = c == '+' ? 1 : -1;
+      val = 0;
+      in_num = TRUE;
+      in_frac = FALSE;
+      in_exp = FALSE;
+      exp = 0;
+      exp_sign = 1;
+      exp_wait_sign = FALSE;
+    } else if (c == 'z' || c == 'Z') {
+      if (ctx->param)
+        s_path_parse_do_cmd (ctx, TRUE);
+      /* s_path_closepath (ctx->path); */
+      /* s_path_lineto (ctx->path, ctx->mpx, ctx->mpy); */
+      s_path_art_finish (ctx->path);
+
+      ctx->cpx = ctx->rpx = ctx->path->sections[ctx->path->num_sections - 1].x3;
+      ctx->cpy = ctx->rpy = ctx->path->sections[ctx->path->num_sections - 1].y3;
+    } else if (c >= 'A' && c <= 'Z' && c != 'E') {
+      if (ctx->param)
+        s_path_parse_do_cmd (ctx, TRUE);
+      ctx->cmd = c + 'a' - 'A';
+      ctx->rel = FALSE;
+    } else if (c >= 'a' && c <= 'z' && c != 'e') {
+      if (ctx->param)
+        s_path_parse_do_cmd (ctx, TRUE);
+      ctx->cmd = c;
+      ctx->rel = TRUE;
+    }
+    /* else c _should_ be whitespace or , */
+  }
+}
+
+
+PATH *s_path_parse (const char *path_str)
+{
+  RSVGParsePathCtx ctx;
+
+  ctx.path = s_path_new ();
+  ctx.cpx = 0.0;
+  ctx.cpy = 0.0;
+  ctx.mpx = 0.0;
+  ctx.mpy = 0.0;
+  ctx.cmd = 0;
+  ctx.param = 0;
+
+  s_path_parse_data (&ctx, path_str);
+
+  if (ctx.param)
+    s_path_parse_do_cmd (&ctx, TRUE);
+
+  return ctx.path;
+}
+
+
+char *s_path_string_from_path (const PATH *path)
+{
+  PATH_SECTION *section;
+  GString *path_string;
+  int i;
+
+  path_string = g_string_new ("");
+
+  for (i = 0; i < path->num_sections; i++) {
+    section = &path->sections[i];
+
+    if (i > 0)
+      g_string_append_c (path_string, ' ');
+
+    switch (section->code) {
+      case PATH_MOVETO:
+        g_string_append_printf (path_string, "M %i,%i",
+                                section->x3, section->y3);
+        break;
+      case PATH_MOVETO_OPEN:
+        g_string_append_printf (path_string, "M %i,%i",
+                                section->x3, section->y3);
+        break;
+      case PATH_CURVETO:
+        g_string_append_printf (path_string, "C %i,%i %i,%i %i,%i",
+                                section->x1, section->y1,
+                                section->x2, section->y2,
+                                section->x3, section->y3);
+        break;
+      case PATH_LINETO:
+        g_string_append_printf (path_string, "L %i,%i",
+                                section->x3, section->y3);
+        break;
+      case PATH_END:
+        g_string_append_printf (path_string, "z");
+        break;
+    }
+  }
+
+  return g_string_free (path_string, FALSE);
+}

commit e9f4a9a4bb05bc554e165b2215f8e7358b2315de
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:57:44 2008 +0100

    Move some static variables inside o_grips.c to GSCHEM_TOPLEVEL
    
    This is necessary for grip manipulation of more complex OBJECT types
    such as the forthcoming PATHs. As the entire drawing state can't of a
    path can't conveniently be stashed in the GSCHEM_TOPLEVEL variables
    such as first_w{x,y}, second_w{x,y} etc., we need to reference the
    OBJECT and grip being manipulated from the drawing functions which
    are outside o_grips.c.

diff --git a/gschem/include/gschem_struct.h b/gschem/include/gschem_struct.h
index d82fe40..b16b4ea 100644
--- a/gschem/include/gschem_struct.h
+++ b/gschem/include/gschem_struct.h
@@ -100,6 +100,10 @@ struct st_gschem_toplevel {
 					   the screen? */
   int magnetic_visible;                 /* Is the magnetic marker visible */
   int net_direction;                    /* bit field to guess the best net direction */
+  int which_grip;                       /* Which grip is being manipulated.
+                                           Its range of values depends on the
+                                           type of object being manipulated. */
+  OBJECT *which_object;                 /* Object being manipulated */
 
   /* --------------------- */
   /* Gschem internal state */
diff --git a/gschem/src/gschem_toplevel.c b/gschem/src/gschem_toplevel.c
index d3f8b3e..8c67769 100644
--- a/gschem/src/gschem_toplevel.c
+++ b/gschem/src/gschem_toplevel.c
@@ -119,6 +119,8 @@ GSCHEM_TOPLEVEL *gschem_toplevel_new ()
   w_current->rubber_visible = 0;
   w_current->magnetic_visible = 0;
   w_current->net_direction = 0;
+  w_current->which_grip = -1;
+  w_current->which_object = NULL;
 
   /* --------------------- */
   /* Gschem internal state */
diff --git a/gschem/src/o_grips.c b/gschem/src/o_grips.c
index c648865..964bf25 100644
--- a/gschem/src/o_grips.c
+++ b/gschem/src/o_grips.c
@@ -41,15 +41,6 @@
   (w)->first_wy > (w)->second_wy ? (w)->first_wy  :			\
   (w)->first_wy+abs((w)->second_wx - (w)->first_wx)/(w)->pixbuf_wh_ratio
 
-/*! \brief
- *  This variable holds the identifier of the grip currently under
- *  modification. Its range of values depends on the type of object.
- */
-static int whichone_changing = -1;
-/*! \brief
- *  This variable holds a pointer on the object under modification.
- */
-static OBJECT *object_changing = NULL;
 
 /*! \brief Check if point is inside grip.
  *  \par Function Description
@@ -466,9 +457,9 @@ OBJECT *o_grips_search_line_world(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
  *  have been found under (<B>w_x</B>,<B>w_y</B>). It returns <B>TRUE</B> if a grip
  *  has been found and modification of the object has been started.
  *
- *  If a grip has been found, this function modifies the global variables
- *  <B>whichone_changing</B> and <B>object_changing</B> with respectively the
- *  identifier of the grip and the object it belongs to.
+ *  If a grip has been found, this function modifies the GSCHEM_TOPLEVEL
+ *  variables <B>which_grip</B> and <B>which_object</B> with the identifier
+ *  of the grip and the object it belongs to respectively.
  *
  *  \param [in]  w_current  The GSCHEM_TOPLEVEL object.
  *  \param [in]  w_x        Current x coordinate of pointer in screen units.
@@ -490,8 +481,8 @@ int o_grips_start(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y)
   if (object == NULL)
     return FALSE;
 
-  whichone_changing = whichone;
-  object_changing = object;
+  w_current->which_grip = whichone;
+  w_current->which_object = object;
 
   /* there is one */
   /* depending on its type, start the modification process */
@@ -799,8 +790,8 @@ void o_grips_start_line(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current,
  *  object according to the mouse position in <B>w_x</B> and <B>w_y</B>.
  *  The grip under modification is updated and the temporary object displayed.
  *
- *  The object under modification is <B>object_changing</B> and the grip
- *  concerned is <B>whichone_changing</B>.
+ *  The object under modification is <B>w_current->which_object</B> and
+ *  the grip concerned is <B>w_current->which_grip</B>.
  *
  *  Depending on the object type, a specific function is used.
  *  It erases the temporary object, updates its internal representation,
@@ -814,32 +805,33 @@ void o_grips_motion(GSCHEM_TOPLEVEL *w_current, int unsnapped_wx, int unsnapped_
 {
   TOPLEVEL *toplevel = w_current->toplevel;
   int w_x, w_y;
+  int grip = w_current->which_grip;
 
   w_x = snap_grid(toplevel, unsnapped_wx);
   w_y = snap_grid(toplevel, unsnapped_wy);
 
   g_assert( w_current->inside_action != 0 );
-  g_return_if_fail( object_changing != NULL );
+  g_return_if_fail( w_current->which_object != NULL );
 
-  switch(object_changing->type) {
+  switch(w_current->which_object->type) {
     case(OBJ_ARC):
     /* erase, update and draw an arc */
-    o_grips_motion_arc(w_current, unsnapped_wx, unsnapped_wy, whichone_changing);
+    o_grips_motion_arc (w_current, unsnapped_wx, unsnapped_wy, grip);
     break;
 
     case(OBJ_BOX):
     /* erase, update and draw a box */
-    o_grips_motion_box(w_current, w_x, w_y, whichone_changing);
+    o_grips_motion_box (w_current, w_x, w_y, grip);
     break;
 
     case(OBJ_PICTURE):
     /* erase, update and draw a box */
-    o_grips_motion_picture(w_current, w_x, w_y, whichone_changing);
+    o_grips_motion_picture (w_current, w_x, w_y, grip);
     break;
 
     case(OBJ_CIRCLE):
     /* erase, update and draw a circle */
-    o_grips_motion_circle(w_current, w_x, w_y, whichone_changing);
+    o_grips_motion_circle(w_current, w_x, w_y, grip);
     break;
 
     case(OBJ_LINE):
@@ -848,7 +840,7 @@ void o_grips_motion(GSCHEM_TOPLEVEL *w_current, int unsnapped_wx, int unsnapped_
     case(OBJ_BUS):
     /* erase, update and draw a line */
     /* same for net, pin and bus as they share the same internal rep. */
-    o_grips_motion_line(w_current, w_x, w_y, whichone_changing);
+    o_grips_motion_line(w_current, w_x, w_y, grip);
     break;
 
     default:
@@ -982,8 +974,8 @@ void o_grips_motion_line(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y, int which
  *  The temporary representation of the object is erased, the object is
  *  modified and finally drawn.
  *
- *  The object under modification is <B>object_changing</B> and the grip
- *  concerned is <B>whichone_changing</B>.
+ *  The object under modification is <B>w_current->which_object</B> and
+ *  the grip concerned is <B>w_current->which_grip</B>.
  *
  *  Depending on the object type, a specific function is used. It erases
  *  the temporary object, updates the object and draws the modified object
@@ -994,9 +986,11 @@ void o_grips_motion_line(GSCHEM_TOPLEVEL *w_current, int w_x, int w_y, int which
 void o_grips_end(GSCHEM_TOPLEVEL *w_current)
 {
   TOPLEVEL *toplevel = w_current->toplevel;
-  OBJECT *object=NULL;
+  OBJECT *object;
+  int grip;
 
-  object = object_changing;
+  object = w_current->which_object;
+  grip = w_current->which_grip;
 
   if (!object) {
     /* actually this is an error condition hack */
@@ -1009,42 +1003,42 @@ void o_grips_end(GSCHEM_TOPLEVEL *w_current)
 
     case(OBJ_ARC):
     /* modify an arc object */
-    o_grips_end_arc(w_current, object, whichone_changing);
+    o_grips_end_arc(w_current, object, grip);
     break;
 
     case(OBJ_BOX):
     /* modify a box object */
-    o_grips_end_box(w_current, object, whichone_changing);
+    o_grips_end_box(w_current, object, grip);
     break;
 
     case(OBJ_PICTURE):
     /* modify a picture object */
-    o_grips_end_picture(w_current, object, whichone_changing);
+    o_grips_end_picture(w_current, object, grip);
     break;
 
     case(OBJ_CIRCLE):
     /* modify a circle object */
-    o_grips_end_circle(w_current, object, whichone_changing);
+    o_grips_end_circle(w_current, object, grip);
     break;
 
     case(OBJ_LINE):
     /* modify a line object */
-    o_grips_end_line(w_current, object, whichone_changing);
+    o_grips_end_line(w_current, object, grip);
     break;
 
     case(OBJ_NET):
       /* modify a net object */
-      o_grips_end_net(w_current, object, whichone_changing);
+      o_grips_end_net(w_current, object, grip);
       break;
 
     case(OBJ_PIN):
       /* modify a pin object */
-      o_grips_end_pin(w_current, object, whichone_changing);
+      o_grips_end_pin(w_current, object, grip);
       break;
 
     case(OBJ_BUS):
       /* modify a bus object */
-      o_grips_end_bus(w_current, object, whichone_changing);
+      o_grips_end_bus(w_current, object, grip);
       break;
 
     default:
@@ -1052,8 +1046,8 @@ void o_grips_end(GSCHEM_TOPLEVEL *w_current)
   }
 
   /* reset global variables */
-  whichone_changing = -1;
-  object_changing = NULL;
+  w_current->which_grip = -1;
+  w_current->which_object = NULL;
 
   w_current->rubber_visible = 0;
 
@@ -1310,8 +1304,8 @@ void o_grips_end_net(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone
   other_objects = s_conn_return_others(other_objects, o_current);
 
   s_conn_remove(toplevel, o_current);
-  o_net_modify(toplevel, o_current, 
-	       w_current->second_wx, w_current->second_wy, whichone_changing);
+  o_net_modify (toplevel, o_current, w_current->second_wx,
+                w_current->second_wy, w_current->which_grip);
   s_conn_update_object(toplevel, o_current);
 
   /* get the other connected objects and redraw them */
@@ -1392,8 +1386,8 @@ void o_grips_end_pin(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone
   other_objects = s_conn_return_others(other_objects, o_current);
 
   s_conn_remove(toplevel, o_current);
-  o_pin_modify(toplevel, o_current, 
-	       w_current->second_wx, w_current->second_wy, whichone_changing);
+  o_pin_modify (toplevel, o_current, w_current->second_wx,
+                w_current->second_wy, w_current->which_grip);
   s_conn_update_object(toplevel, o_current);
   o_redraw_single(w_current, o_current);
 
@@ -1458,8 +1452,8 @@ void o_grips_end_bus(GSCHEM_TOPLEVEL *w_current, OBJECT *o_current, int whichone
   other_objects = s_conn_return_others(other_objects, o_current);
   s_conn_remove(toplevel, o_current);
 
-  o_bus_modify(toplevel, o_current, 
-	       w_current->second_wx, w_current->second_wy, whichone_changing);
+  o_bus_modify (toplevel, o_current, w_current->second_wx,
+                w_current->second_wy, w_current->which_grip);
   s_conn_update_object(toplevel, o_current);
   o_redraw_single(w_current, o_current);
 

commit 699f46d8a598c1cfa972f0ddf80e39d18c907ece
Author: Peter Clifton <pcjc2@xxxxxxxxx>
Date:   Fri Sep 26 00:57:37 2008 +0100

    Fix f_print_set_line_width() to emit valid postscript
    
    Previously this function emitted "%d mils setlinewidth\n". The "mils"
    portion seems to be left over from previous printing code, as "mils"
    isn't defined to be anything in prolog.ps, nor in the headers we emit.
    Our units are scaled to mils, so we can just emit "%d setlinewidth\n".

diff --git a/libgeda/src/f_print.c b/libgeda/src/f_print.c
index 1820544..97b9635 100644
--- a/libgeda/src/f_print.c
+++ b/libgeda/src/f_print.c
@@ -46,7 +46,7 @@ static void f_print_unicode_map(FILE * fp, int count, gunichar * table);
 void f_print_set_line_width(FILE *fp, int width)
 {
   if (width > 0) {
-    fprintf(fp, "%d mils setlinewidth\n", width);
+    fprintf(fp, "%d setlinewidth\n", width);
   }
 }
 




_______________________________________________
geda-cvs mailing list
geda-cvs@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-cvs