private static SketchDocument importSVG(InputStream stream) throws Exception {
   SketchDocument sdoc = new SketchDocument();
   sdoc.removePage(sdoc.getCurrentPage());
   SketchDocument.SketchPage page = sdoc.addPage();
   u.p("parsing");
   Doc doc = XMLParser.parse(stream);
   u.p("parsed");
   Elem svg = doc.xpathElement("/svg");
   for (Elem n : svg.xpath("./*")) {
     u.p("node = " + n + " " + n.name());
     SNode node = loadNode(n);
     if (node != null) page.add(node);
   }
   return sdoc;
 }
 private static void parseFill(SShape shape, Elem root) {
   if (!root.hasAttr("fill")) {
     shape.setFillPaint(FlatColor.BLACK);
     return;
   }
   String sfill = root.attr("fill");
   if (sfill.equals("none")) {
     shape.setFillPaint(null);
     return;
   }
   if (sfill.startsWith("#")) {
     shape.setFillPaint(new FlatColor(sfill));
     return;
   }
   u.p("trouble parsing fill: " + sfill);
 }
  private static void parseStroke(SShape shape, Elem root) {
    if (!root.hasAttr("stroke")) {
      shape.setStrokeWidth(1);
      shape.setStrokePaint(null);
    }
    String sstroke = root.attr("stroke");
    if (sstroke.equals("none")) {
      shape.setStrokePaint(null);
    }
    if (sstroke.startsWith("#")) {
      shape.setStrokePaint(new FlatColor(sstroke));
    }

    if (!root.hasAttr("stroke-width")) {
      shape.setStrokeWidth(1);
    } else {
      String sstrokeWidth = root.attr("stroke-width");
      double strokeWidth = Double.parseDouble(sstrokeWidth);
      shape.setStrokeWidth(strokeWidth);
    }
  }
  private static SNode loadNode(Elem root) throws Exception {
    u.p("loading SVG element: " + root.name());
    if ("g".equals(root.name())) {
      SGroup g = new SGroup();
      for (Elem n : root.xpath("./*")) {
        SNode nn = loadNode(n);
        if (nn != null) g.addAll(false, nn);
      }
      g.normalize();
      return g;
    }
    if ("rect".equals(root.name())) {
      SRect rect = new SRect();
      rect.setX(Double.parseDouble(root.attr("x")));
      rect.setY(Double.parseDouble(root.attr("y")));
      parseFill(rect, root);
      parseStroke(rect, root);
      rect.setWidth(Double.parseDouble(root.attr("width")));
      rect.setHeight(Double.parseDouble(root.attr("height")));
      return rect;
    }
    if ("line".equals(root.name())) {
      SPoly poly = new SPoly();
      double x1 = Double.parseDouble(root.attr("x1"));
      double y1 = Double.parseDouble(root.attr("y1"));
      double x2 = Double.parseDouble(root.attr("x2"));
      double y2 = Double.parseDouble(root.attr("y2"));
      poly.addPoint(new Point2D.Double(x1, y1));
      poly.addPoint(new Point2D.Double(x2, y2));
      poly.setClosed(false);
      parseFill(poly, root);
      parseStroke(poly, root);
      return poly;
    }
    if ("polygon".equals(root.name()) || "polyline".equals(root.name())) {
      String pointsString = root.attr("points");
      String[] points = pointsString.split("\\s");
      SPoly poly = new SPoly();
      for (String pt : points) {
        if (pt != null && pt.trim().equals("")) continue;
        String[] xy = pt.split(",");
        poly.addPoint(new Point2D.Double(Double.parseDouble(xy[0]), Double.parseDouble(xy[1])));
      }
      // the polygon is closed, poly lines aren't
      poly.setClosed("polygon".equals(root.name()));
      parseFill(poly, root);
      parseStroke(poly, root);
      return poly;
    }
    if ("path".equals(root.name())) {
      return parsePathNode(root);
    }

    Exception ex = new Exception("unrecognized SVG element: " + root.name());
    u.p(ex);
    return null;
  }
  private static SNode parsePathNode(Elem root) throws IOException {
    SPath path = new SPath();
    String d = root.attr("d");

    PushbackReader read = new PushbackReader(new StringReader(d));
    int count = 0;
    double x = 0;
    double y = 0;
    SPath.PathPoint prev = null;
    boolean go = true;
    while (go) {
      count++;
      char ch = (char) read.read();
      if (ch == -1) break;
      double x1, x2, y1, y2;
      u.p("ch = " + ch);
      switch (ch) {
          // absolute move
        case 'M':
          x = readDouble(read);
          y = readDouble(read);
          prev = path.moveTo(x, y);
          continue;
          // relative vertical lineto
        case 'v':
          y += readDouble(read);
          prev = path.lineTo(x, y);
          continue;
          // absolute vertical lineto
        case 'V':
          y = readDouble(read);
          prev = path.lineTo(x, y);
          continue;
          // relative horiz lineto
        case 'h':
          x += readDouble(read);
          prev = path.lineTo(x, y);
          continue;
        case 'H':
          x = readDouble(read);
          prev = path.lineTo(x, y);
          continue;
          // relative lineto
        case 'l':
          x += readDouble(read);
          y += readDouble(read);
          prev = path.lineTo(x, y);
          continue;
        case 'L':
          x = readDouble(read);
          y = readDouble(read);
          prev = path.lineTo(x, y);
          continue;
          // relative cubic curve
        case 'c':
          x1 = x + readDouble(read);
          y1 = y + readDouble(read);
          x2 = x + readDouble(read);
          y2 = y + readDouble(read);
          x += readDouble(read);
          y += readDouble(read);
          prev = path.curveTo(prev, x1, y1, x2, y2, x, y);
          continue;
          // relative shorthand curve
        case 's':
          x2 = x + readDouble(read);
          y2 = y + readDouble(read);
          x += readDouble(read);
          y += readDouble(read);
          double dx = prev.x - prev.cx1;
          double dy = prev.y - prev.cy1;
          double rx = prev.x + dx;
          double ry = prev.y + dy;
          prev = path.curveTo(prev, rx, ry, x2, y2, x, y);
          continue;
          // absolute cubic curve
        case 'C':
          x1 = readDouble(read);
          y1 = readDouble(read);
          x2 = readDouble(read);
          y2 = readDouble(read);
          x = readDouble(read);
          y = readDouble(read);
          prev = path.curveTo(prev, x1, y1, x2, y2, x, y);
          continue;
        case 'z':
          path.close();
          break;
        case ' ':
          continue;
        case '\n':
          continue;
          // end of string
        case (char) -1:
          go = false;
          break;
        default:
          u.p("unrecognized character! " + ch + " " + ((int) ch));
          go = false;
          break;
      }
    }

    parseFill(path, root);
    parseStroke(path, root);
    return path;
  }