summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWerner Almesberger <werner@almesberger.net>2016-08-02 11:00:08 -0300
committerWerner Almesberger <werner@almesberger.net>2016-08-02 11:00:08 -0300
commit8745b40e3eb216fc5cfa7373fea3b4ae991f1c3e (patch)
treeeab2aa7ec1941bd3da99f1dbcf64eab04d34667a
downloadeeshow-8745b40e3eb216fc5cfa7373fea3b4ae991f1c3e.tar.gz
eeshow-8745b40e3eb216fc5cfa7373fea3b4ae991f1c3e.tar.bz2
eeshow-8745b40e3eb216fc5cfa7373fea3b4ae991f1c3e.zip
rename sch2fig to eeshow
-rw-r--r--DEMO25
-rw-r--r--Makefile91
-rw-r--r--TODO24
-rw-r--r--cro.c461
-rw-r--r--cro.h31
-rw-r--r--diff.c279
-rw-r--r--diff.h22
-rw-r--r--dwg.c463
-rw-r--r--dwg.h46
-rw-r--r--fig.c293
-rw-r--r--fig.h22
-rw-r--r--file.c76
-rw-r--r--file.h22
-rw-r--r--gfx.c120
-rw-r--r--gfx.h72
-rw-r--r--git-file.c371
-rw-r--r--git-file.h22
-rw-r--r--lib-parse.c268
-rw-r--r--lib-render.c396
-rw-r--r--lib.h124
-rw-r--r--main.c172
-rw-r--r--main.h31
-rw-r--r--misc.c114
-rw-r--r--misc.h51
-rw-r--r--neo900-template.fig15
-rw-r--r--record.c342
-rw-r--r--record.h58
-rw-r--r--sch-parse.c562
-rw-r--r--sch-render.c174
-rw-r--r--sch.h123
-rwxr-xr-xsch2pdf92
-rw-r--r--style.c31
-rw-r--r--style.h91
-rw-r--r--test.lib62
-rw-r--r--test.pro34
-rw-r--r--test.sch372
-rw-r--r--test/README24
-rwxr-xr-xtest/comp29
-rwxr-xr-xtest/genpng37
-rw-r--r--text.c158
-rw-r--r--text.h58
-rw-r--r--util.h52
42 files changed, 5910 insertions, 0 deletions
diff --git a/DEMO b/DEMO
new file mode 100644
index 0000000..dc307db
--- /dev/null
+++ b/DEMO
@@ -0,0 +1,25 @@
+# Prerequisites (depends on distribution)
+
+apt-get install libcairo2-dev
+apt-get install libgit2-dev
+apt-get install qiv
+
+# Get all the things we need
+
+git clone http://neo900.org/git/ee.git newdir
+cd newdir/hw
+git clone git://projects.qi-hardware.com/kicad-libs.git
+git clone git://projects.qi-hardware.com/eda-tools.git
+make -C eda-tools/eeshow
+
+# Generate PNG for old, new, and difference
+
+LIBS="neo900.lib kicad-libs/components/powered.lib"
+eda-tools/eeshow/eeshow $LIBS 6a9f71:neo900_SS_5.sch -- png -s 2 -o old.png
+eda-tools/eeshow/eeshow $LIBS neo900_SS_5.sch -- png -s 2 -o new.png
+eda-tools/eeshow/eeshow $LIBS 6a9f71:neo900_SS_5.sch -- \
+ diff -s 2 -o diff.png $LIBS neo900_SS_5.sch
+
+# View the result
+
+qiv -t diff.png old.png new.png
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1fa2b28
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,91 @@
+#
+# Makefile - build eeshow
+#
+# Written 2016 by Werner Almesberger
+# Copyright 2016 by Werner Almesberger
+#
+# 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.
+#
+
+NAME = eeshow
+OBJS = main.o sch-parse.o sch-render.o lib-parse.o lib-render.o \
+ file.o git-file.o \
+ style.o fig.o record.o cro.o diff.o gfx.o dwg.o text.o misc.o
+
+CFLAGS = -g -Wall -Wextra -Wno-unused-parameter -Wshadow \
+ -Wmissing-prototypes -Wmissing-declarations \
+ `pkg-config --cflags cairo` \
+ `pkg-config --cflags libgit2`
+LDLIBS = -lm \
+ `pkg-config --libs cairo` \
+ `pkg-config --libs libgit2`
+
+include ../common/Makefile.c-common
+
+.PHONY: test neo900 sch test testref png pngref diff view newref
+
+all:: $(NAME)
+
+$(NAME): $(OBJS)
+ $(CC) -o $(NAME) $(OBJS) $(LDLIBS)
+
+#----- Test sheet -------------------------------------------------------------
+
+sch:
+ eeschema test.sch
+
+test: $(NAME)
+ ./$(NAME) test.lib test.sch >out.fig
+ fig2dev -L png -m 2 out.fig _out.png
+ [ ! -r ref.png ] || \
+ compare -metric AE ref.png _out.png _diff.png || \
+ qiv -t -R -D _diff.png ref.png _out.png
+
+testref: $(NAME)
+ ./$(NAME) test.lib test.sch | fig2dev -L png -m 2 >ref.png
+
+png: $(NAME)
+ ./$(NAME) test.lib test.sch -- png -o _out.png -s 2
+ [ ! -r pngref.png ] || \
+ compare -metric AE pngref.png _out.png _diff.png || \
+ qiv -t -R -D _diff.png pngref.png _out.png
+
+pngref: $(NAME)
+ ./$(NAME) test.lib test.sch -- png -o pngref.png -s 2
+
+clean::
+ rm -f out.fig _out.png _diff.png
+
+#----- Render Neo900 schematics -----------------------------------------------
+
+NEO900_HW = ../../../n9/ee/hw
+KICAD_LIBS = ../../kicad-libs/components
+
+SHEET ?= 12
+
+neo900: $(NAME)
+ ./$(NAME) $(NEO900_HW)/neo900.lib \
+ $(KICAD_LIBS)/powered.lib \
+ $(NEO900_HW)/neo900_SS_$(SHEET).sch \
+ >out.fig
+
+neo900.pdf: $(NAME) sch2pdf neo900-template.fig
+ ./sch2pdf -o $@ -t neo900-template.fig \
+ $(NEO900_HW)/neo900.lib $(KICAD_LIBS)/powered.lib \
+ $(NEO900_HW)/neo900.sch
+
+#----- Regression test based on Neo900 schematics -----------------------------
+
+diff: $(NAME)
+ test/genpng test out
+ test/comp test || $(MAKE) view
+
+view:
+ qiv -t -R -D `echo test/_diff*.png | \
+ sed 's/\([^ ]*\)_diff\([^ ]*\)/\1_diff\2 \1ref\2 \1out\2/g'`
+
+newref:
+ test/genpng test ref
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..a2e2899
--- /dev/null
+++ b/TODO
@@ -0,0 +1,24 @@
+- better text size guessing also for FIG
+- unify alignment, direction
+- support fonts attributes ?
+- support line thickness ?
+- ~ as overline (grep for ~ in out.fig)
+- glabel: build for "right" style, then rotate poly (like hlabel)
+- show open pins / wires
+- check remaining alignment / direction / rotation cases in switch statements
+- support mirroring (and detect-complain if unexpected) [should be done now]
+- pin shapes (inverted, clock, etc.)
+- optionally display pin type
+- find libraries (e.g., from .pro)
+- PDF TOC
+- let user set PNG size or zoom level
+- parse .kicad_wks
+- on parse error, politely complain, don't terminate;
+ convert abort / assert(0) to proper error indications
+- implement destructors
+- check for memory leaks
+- record.c (bb_rot): implement bounding boxes for text
+- nesting gfx in diff is a huge kludge, caused by global vars in gfx.c
+- move path name guessing into file.c
+- return indication of whether diff found any differences
+- in diff, pass only options understood by cairo_png
diff --git a/cro.c b/cro.c
new file mode 100644
index 0000000..8ede2b9
--- /dev/null
+++ b/cro.c
@@ -0,0 +1,461 @@
+/*
+ * cro.c - Cairo graphics back-end
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#include <stddef.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <math.h>
+
+#include <cairo/cairo.h>
+#include <cairo/cairo-pdf.h>
+
+#include "util.h"
+#include "style.h"
+#include "text.h"
+#include "gfx.h"
+#include "record.h"
+#include "main.h"
+#include "cro.h"
+
+
+/*
+ * FIG works with 1/1200 in
+ * KiCad works with mil
+ * 1 point = 1/72 in
+ */
+
+#define DEFAULT_SCALE (72.0 / 1200)
+
+
+struct cro_ctx {
+ struct record record; /* must be first */
+
+ int xo, yo;
+ float scale;
+
+ cairo_t *cr;
+ cairo_surface_t *s;
+
+ struct record *sheets; /* for PDF */
+ unsigned n_sheets;
+
+ const char *output_name;
+};
+
+
+static inline int cd(struct cro_ctx *cc, int x)
+{
+ return x * cc->scale;
+}
+
+
+static inline int cx(struct cro_ctx *cc, int x)
+{
+ return cc->xo + x * cc->scale;
+}
+
+
+static inline int xc(struct cro_ctx *cc, int x)
+{
+ return (x - cc->xo) / cc->scale;
+}
+
+
+static inline int cy(struct cro_ctx *cc, int y)
+{
+ return cc->yo + y * cc->scale;
+}
+
+
+static inline float pt(struct cro_ctx *cc, int x)
+{
+ return cd(cc, x) * 72 * 1.5 / 1200.0;
+}
+
+
+static void set_color(cairo_t *cr, int color)
+{
+ uint32_t c;
+
+ if (color < 0)
+ return;
+ c = color_rgb[color];
+ cairo_set_source_rgb(cr, (c >> 16) / 255.0, ((c >> 8) & 255) / 255.0,
+ (c & 255) / 255.0);
+}
+
+
+static void paint(cairo_t *cr, int color, int fill_color)
+{
+ if (fill_color != COLOR_NONE) {
+ set_color(cr, fill_color);
+ if (color == COLOR_NONE)
+ cairo_fill(cr);
+ else
+ cairo_fill_preserve(cr);
+ }
+ if (color != COLOR_NONE) {
+ set_color(cr, color);
+ cairo_stroke(cr);
+ }
+}
+
+
+/* ----- General items ----------------------------------------------------- */
+
+
+static void cr_line(void *ctx, int sx, int sy, int ex, int ey,
+ int color, unsigned layer)
+{
+ struct cro_ctx *cc = ctx;
+ static const double dashes[] = { 4, 2 };
+
+ cairo_new_path(cc->cr);
+ cairo_move_to(cc->cr, cx(cc, sx), cy(cc, sy));
+ cairo_line_to(cc->cr, cx(cc, ex), cy(cc, ey));
+ cairo_set_dash(cc->cr, dashes, ARRAY_ELEMENTS(dashes), 0);
+ paint(cc->cr, color, COLOR_NONE);
+ cairo_set_dash(cc->cr, NULL, 0, 0);
+}
+
+
+static void cr_poly(void *ctx,
+ int points, const int x[points], const int y[points],
+ int color, int fill_color, unsigned layer)
+{
+ struct cro_ctx *cc = ctx;
+ bool closed;
+ int i;
+
+ if (points < 2)
+ return;
+ closed = x[0] == x[points - 1] && y[0] == y[points - 1];
+
+ cairo_new_path(cc->cr);
+ cairo_move_to(cc->cr, cx(cc, x[0]), cy(cc, y[0]));
+
+ for (i = 1; i != points - closed; i++)
+ cairo_line_to(cc->cr, cx(cc, x[i]), cy(cc, y[i]));
+ if (closed)
+ cairo_close_path(cc->cr);
+
+ paint(cc->cr, color, fill_color);
+}
+
+
+static void cr_circ(void *ctx, int x, int y, int r,
+ int color, int fill_color, unsigned layer)
+{
+ struct cro_ctx *cc = ctx;
+
+ cairo_new_path(cc->cr);
+ cairo_arc(cc->cr, cx(cc, x), cy(cc, y), cd(cc, r), 0, 2 * M_PI);
+ paint(cc->cr, color, fill_color);
+}
+
+
+static void cr_arc(void *ctx, int x, int y, int r, int sa, int ea,
+ int color, int fill_color, unsigned layer)
+{
+ struct cro_ctx *cc = ctx;
+
+ cairo_new_path(cc->cr);
+ cairo_arc(cc->cr, cx(cc, x), cy(cc, y), cd(cc, r),
+ -ea / 180.0 * M_PI, -sa / 180.0 * M_PI);
+ paint(cc->cr, color, fill_color);
+}
+
+
+#define TEXT_STRETCH 1.3
+
+
+static void cr_text(void *ctx, int x, int y, const char *s, unsigned size,
+ enum text_align align, int rot, unsigned color, unsigned layer)
+{
+ struct cro_ctx *cc = ctx;
+ cairo_text_extents_t ext;
+ cairo_matrix_t m;
+
+ cairo_set_font_size(cc->cr, cd(cc, size) * TEXT_STRETCH);
+ cairo_text_extents(cc->cr, s, &ext);
+
+ set_color(cc->cr, color);
+
+ cairo_move_to(cc->cr, cx(cc, x), cy(cc, y));
+
+ cairo_get_matrix(cc->cr, &m);
+ cairo_rotate(cc->cr, -rot / 180.0 * M_PI);
+
+ switch (align) {
+ case text_min:
+ break;
+ case text_mid:
+ cairo_rel_move_to(cc->cr, -ext.width / 2.0, 0);
+ break;
+ case text_max:
+ cairo_rel_move_to(cc->cr, -ext.width, 0);
+ break;
+ default:
+ abort();
+ }
+
+ cairo_show_text(cc->cr, s);
+ cairo_set_matrix(cc->cr, &m);
+}
+
+
+static unsigned cr_text_width(void *ctx, const char *s, unsigned size)
+{
+ struct cro_ctx *cc = ctx;
+ cairo_text_extents_t ext;
+
+ cairo_set_font_size(cc->cr, cx(cc, size) * TEXT_STRETCH);
+ cairo_text_extents(cc->cr, s, &ext);
+ return xc(cc, ext.width) * 1.05; /* @@@ Cairo seems to underestimate */
+}
+
+
+/* ----- Initializatio and termination ------------------------------------- */
+
+
+static const struct gfx_ops real_cro_ops = {
+ .name = "cairo",
+ .line = cr_line,
+ .poly = cr_poly,
+ .circ = cr_circ,
+ .arc = cr_arc,
+ .text = cr_text,
+ .text_width = cr_text_width,
+};
+
+
+static struct cro_ctx *init_common(int argc, char *const *argv)
+{
+ struct cro_ctx *cc;
+ char c;
+
+ cc = alloc_type(struct cro_ctx);
+ cc->xo = cc->yo = 0;
+ cc->scale = DEFAULT_SCALE;
+
+ cc->sheets = NULL;
+ cc->n_sheets = 0;
+
+ cc->output_name = NULL;
+ while ((c = getopt(argc, argv, "o:s:")) != EOF)
+ switch (c) {
+ case 'o':
+ cc->output_name = optarg;
+ break;
+ case 's':
+ cc->scale = atof(optarg) * DEFAULT_SCALE;
+ break;
+ default:
+ usage(*argv);
+ }
+
+ record_init(&cc->record, &real_cro_ops, cc);
+
+ return cc;
+}
+
+
+static void *cr_png_init(int argc, char *const *argv)
+{
+ struct cro_ctx *cc;
+
+ cc = init_common(argc, argv);
+
+ /* cr_text_width needs *something* to work with */
+
+ cc->s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 16, 16);
+ cc->cr = cairo_create(cc->s);
+
+ return cc;
+}
+
+
+static void *cr_pdf_init(int argc, char *const *argv)
+{
+ struct cro_ctx *cc;
+
+ cc = init_common(argc, argv);
+
+ /* cr_text_width needs *something* to work with */
+
+ cc->s = cairo_pdf_surface_create(NULL, 16, 16);
+ cc->cr = cairo_create(cc->s);
+
+ return cc;
+}
+
+
+static void end_common(struct cro_ctx *cc, int *w, int *h)
+{
+ int x, y;
+
+ cairo_surface_destroy(cc->s);
+ cairo_destroy(cc->cr);
+
+ record_bbox(&cc->record, &x, &y, w, h);
+
+// fprintf(stderr, "%dx%d%+d%+d\n", *w, *h, x, y);
+ cc->xo = -cd(cc, x);
+ cc->yo = -cd(cc, y);
+ *w = cd(cc, *w);
+ *h = cd(cc, *h);
+// fprintf(stderr, "%dx%d%+d%+d\n", *w, *h, x, y);
+}
+
+
+static void cr_png_end(void *ctx)
+{
+ struct cro_ctx *cc = ctx;
+ int w, h;
+
+ end_common(cc, &w, &h);
+
+ cc->s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, w, h);
+ cc->cr = cairo_create(cc->s);
+
+ set_color(cc->cr, COLOR_WHITE);
+ cairo_paint(cc->cr);
+
+ cairo_select_font_face(cc->cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_line_width(cc->cr, 2);
+
+ record_replay(&cc->record);
+ record_destroy(&cc->record);
+
+ if (cc->output_name)
+ cairo_surface_write_to_png(cc->s, cc->output_name);
+}
+
+
+static void cr_pdf_new_sheet(void *ctx)
+{
+ struct cro_ctx *cc = ctx;
+
+ cc->n_sheets++;
+ cc->sheets = realloc(cc->sheets, sizeof(struct record) * cc->n_sheets);
+ if (!cc->sheets) {
+ perror("realloc");
+ exit(1);
+ }
+ cc->sheets[cc->n_sheets - 1] = cc->record;
+ record_wipe(&cc->record);
+}
+
+
+static void cr_pdf_end(void *ctx)
+{
+ struct cro_ctx *cc = ctx;
+ int w, h;
+ unsigned i;
+
+ end_common(cc, &w, &h);
+
+ cc->s = cairo_pdf_surface_create(cc->output_name, w, h);
+ cc->cr = cairo_create(cc->s);
+
+ cairo_select_font_face(cc->cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_line_width(cc->cr, 2);
+
+ for (i = 0; i != cc->n_sheets; i++) {
+ set_color(cc->cr, COLOR_WHITE);
+ cairo_paint(cc->cr);
+
+ record_replay(cc->sheets + i);
+ record_destroy(cc->sheets + i);
+
+ cairo_show_page(cc->cr);
+ }
+
+ record_replay(&cc->record);
+ record_destroy(&cc->record);
+
+ cairo_show_page(cc->cr);
+
+ cairo_surface_destroy(cc->s);
+ cairo_destroy(cc->cr);
+}
+
+
+uint32_t *cro_img_end(void *ctx, int *w, int *h, int *stride)
+{
+ struct cro_ctx *cc = ctx;
+ uint32_t *data;
+
+ end_common(cc, w, h);
+
+ *stride = cairo_format_stride_for_width(CAIRO_FORMAT_RGB24, *w);
+ data = alloc_size(*stride * *h);
+
+ cc->s = cairo_image_surface_create_for_data((unsigned char *) data,
+ CAIRO_FORMAT_RGB24, *w, *h, *stride);
+ cc->cr = cairo_create(cc->s);
+
+ set_color(cc->cr, COLOR_WHITE);
+ cairo_paint(cc->cr);
+
+ cairo_select_font_face(cc->cr, "Helvetica", CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ cairo_set_line_width(cc->cr, 2);
+
+ record_replay(&cc->record);
+ record_destroy(&cc->record);
+
+ return data;
+}
+
+
+void cro_img_write(void *ctx, const char *name)
+{
+ struct cro_ctx *cc = ctx;
+
+ cairo_surface_write_to_png(cc->s, name);
+}
+
+
+/* ----- Operations -------------------------------------------------------- */
+
+
+const struct gfx_ops cro_png_ops = {
+ .name = "png",
+ .line = record_line,
+ .poly = record_poly,
+ .circ = record_circ,
+ .arc = record_arc,
+ .text = record_text,
+ .text_width = cr_text_width,
+ .init = cr_png_init,
+ .end = cr_png_end,
+};
+
+const struct gfx_ops cro_pdf_ops = {
+ .name = "pdf",
+ .line = record_line,
+ .poly = record_poly,
+ .circ = record_circ,
+ .arc = record_arc,
+ .text = record_text,
+ .text_width = cr_text_width,
+ .init = cr_pdf_init,
+ .new_sheet = cr_pdf_new_sheet,
+ .end = cr_pdf_end,
+};
diff --git a/cro.h b/cro.h
new file mode 100644
index 0000000..3595da8
--- /dev/null
+++ b/cro.h
@@ -0,0 +1,31 @@
+/*
+ * cro.h - Cairo graphics back-end
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#ifndef CRO_H
+#define CRO_H
+
+#include <stdint.h>
+
+#include "gfx.h"
+
+
+extern const struct gfx_ops cro_png_ops;
+extern const struct gfx_ops cro_pdf_ops;
+
+#define cro_img_ops cro_png_ops /* just don't call cro_img_ops.end */
+
+
+uint32_t *cro_img_end(void *ctx, int *w, int *h, int *stride);
+void cro_img_write(void *ctx, const char *name);
+
+#endif /* !CRO_H */
diff --git a/diff.c b/diff.c
new file mode 100644
index 0000000..8f7d4a0
--- /dev/null
+++ b/diff.c
@@ -0,0 +1,279 @@
+/*
+ * diff.c - Schematics difference
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "main.h"
+#include "cro.h"
+#include "sch.h"
+#include "lib.h"
+#include "diff.h"
+
+
+#define DEFAULT_FRAME_RADIUS 30
+
+
+struct area {
+ int xa, ya, xb, yb;
+ struct area *next;
+};
+
+struct diff {
+ void *cr_ctx;
+ uint32_t *new_img;
+ int w, h, stride;
+ const char *output_name;
+ int frame_radius;
+ struct area *areas;
+};
+
+
+/* ----- Wrappers ---------------------------------------------------------- */
+
+
+static void diff_line(void *ctx, int sx, int sy, int ex, int ey,
+ int color, unsigned layer)
+{
+ const struct diff *diff = ctx;
+
+ cro_img_ops.line(diff->cr_ctx, sx, sy, ex, ey, color, layer);
+}
+
+
+static void diff_poly(void *ctx,
+ int points, const int x[points], const int y[points],
+ int color, int fill_color, unsigned layer)
+{
+ const struct diff *diff = ctx;
+
+ cro_img_ops.poly(diff->cr_ctx, points, x, y, color, fill_color, layer);
+}
+
+
+static void diff_circ(void *ctx, int x, int y, int r,
+ int color, int fill_color, unsigned layer)
+{
+ const struct diff *diff = ctx;
+
+ cro_img_ops.circ(diff->cr_ctx, x, y, r, color, fill_color, layer);
+}
+
+
+static void diff_arc(void *ctx, int x, int y, int r, int sa, int ea,
+ int color, int fill_color, unsigned layer)
+{
+ const struct diff *diff = ctx;
+
+ cro_img_ops.arc(diff->cr_ctx, x, y, r, sa, ea,
+ color, fill_color, layer);
+}
+
+
+static void diff_text(void *ctx, int x, int y, const char *s, unsigned size,
+ enum text_align align, int rot, unsigned color, unsigned layer)
+{
+ const struct diff *diff = ctx;
+
+ cro_img_ops.text(diff->cr_ctx, x, y, s, size, align, rot,
+ color, layer);
+}
+
+
+static unsigned diff_text_width(void *ctx, const char *s, unsigned size)
+{
+ const struct diff *diff = ctx;
+
+ return cro_img_ops.text_width(diff->cr_ctx, s, size);
+}
+
+
+/* ----- Initialization and termination ------------------------------------ */
+
+
+static void *diff_init(int argc, char *const *argv)
+{
+ struct diff *diff;
+ char c;
+ int arg;
+ struct sch_ctx new_sch;
+ struct lib new_lib;
+
+ diff = alloc_type(struct diff);
+ diff->areas = NULL;
+
+ sch_init(&new_sch, 0);
+ lib_init(&new_lib);
+
+ diff->output_name = NULL;
+ diff->frame_radius = DEFAULT_FRAME_RADIUS;
+ while ((c = getopt(argc, argv, "o:s:")) != EOF)
+ switch (c) {
+ case 'o':
+ diff->output_name = optarg;
+ break;
+ case 's':
+ /* for cro_png */
+ break;
+ default:
+ usage(*argv);
+ }
+
+ if (argc - optind < 1)
+ usage(*argv);
+
+ for (arg = optind; arg != argc - 1; arg++)
+ lib_parse(&new_lib, argv[arg]);
+ sch_parse(&new_sch, argv[argc - 1], &new_lib);
+
+ optind = 0;
+ gfx_init(&cro_img_ops, argc, argv);
+ diff->cr_ctx = gfx_ctx;
+ sch_render(new_sch.sheets);
+ diff->new_img = cro_img_end(gfx_ctx,
+ &diff->w, &diff->h, &diff->stride);
+
+ optind = 0;
+ diff->cr_ctx = cro_img_ops.init(argc, argv);
+
+ return diff;
+}
+
+
+/* steal from schhist/ppmdiff.c */
+
+#define ONLY_OLD 0xff0000
+#define ONLY_NEW 0x00d000
+#define BOTH 0x707070
+
+#define AREA_FILL 0xffffc8
+
+
+static void mark_area(struct diff *diff, int x, int y)
+{
+ struct area *area;
+
+ for (area = diff->areas; area; area = area->next)
+ if (x >= area->xa && x <= area->xb &&
+ y >= area->ya && y <= area->yb) {
+ if (area->xa > x - diff->frame_radius)
+ area->xa = x - diff->frame_radius;
+ if (area->xb < x + diff->frame_radius)
+ area->xb = x + diff->frame_radius;
+ if (area->ya > y - diff->frame_radius)
+ area->ya = y - diff->frame_radius;
+ if (area->yb < y + diff->frame_radius)
+ area->yb = y + diff->frame_radius;
+ return;
+ }
+
+ area = alloc_type(struct area);
+
+ area->xa = x - diff->frame_radius;
+ area->xb = x + diff->frame_radius;
+ area->ya = y - diff->frame_radius;
+ area->yb = y + diff->frame_radius;
+
+ area->next = diff->areas;
+ diff->areas = area;
+}
+
+
+#define MASK 0xffffff
+
+
+static void differences(struct diff *diff, uint32_t *a, const uint32_t *b)
+{
+ int x, y;
+ unsigned skip = diff->w * 4 - diff->stride;
+
+ for (y = 0; y != diff->h; y++) {
+ for (x = 0; x != diff->w; x++) {
+ if (!((*a ^ *b) & MASK)) {
+ *a = ((*a >> 3) & 0x1f1f1f) | 0xe0e0e0;
+// *a = ((*a >> 2) & 0x3f3f3f) | 0xc0c0c0;
+ } else {
+ mark_area(diff, x, y);
+//fprintf(stderr, "0x%06x 0x%06x", *a, *b);
+ *a = (*a & MASK) == MASK ? ONLY_NEW :
+ (*b & MASK) == MASK ? ONLY_OLD : BOTH;
+//fprintf(stderr, "-> 0x%06x\n", *a);
+ }
+ a++;
+ b++;
+ }
+ a += skip;
+ b += skip;
+ }
+}
+
+
+static void show_areas(struct diff *diff, uint32_t *a)
+{
+ const struct area *area;
+ uint32_t *p;
+ int x, y;
+
+ for (area = diff->areas; area; area = area->next)
+ for (y = area->ya; y != area->yb; y++) {
+ if (y < 0 || y >= diff->h)
+ continue;
+ p = a + y * (diff->stride >> 2);
+ for (x = area->xa; x != area->xb; x++) {
+ if (x >= 0 && x < diff->w &&
+ (p[x] & MASK) == MASK)
+ p[x] = AREA_FILL;
+ }
+ }
+}
+
+
+static void diff_end(void *ctx)
+{
+ struct diff *diff = ctx;
+ uint32_t *old_img;
+ int w, h, stride;
+
+ old_img = cro_img_end(diff->cr_ctx, &w, &h, &stride);
+ if (diff->w != w || diff->h != h) {
+ fprintf(stderr, "%d x %d vs. %d x %d image\n",
+ w, h, diff->w, diff->h);
+ exit(1);
+ }
+
+ differences(diff, old_img, diff->new_img);
+ show_areas(diff, old_img);
+
+ if (diff->output_name)
+ cro_img_write(diff->cr_ctx, diff->output_name);
+}
+
+
+/* ----- Operations -------------------------------------------------------- */
+
+
+const struct gfx_ops diff_ops = {
+ .name = "diff",
+ .line = diff_line,
+ .poly = diff_poly,
+ .circ = diff_circ,
+ .arc = diff_arc,
+ .text = diff_text,
+ .text_width = diff_text_width,
+ .init = diff_init,
+ .end = diff_end,
+};
diff --git a/diff.h b/diff.h
new file mode 100644
index 0000000..22626b1
--- /dev/null
+++ b/diff.h
@@ -0,0 +1,22 @@
+/*
+ * diff.h - Schematics difference
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#ifndef DIFF_H
+#define DIFF_H
+
+#include "gfx.h"
+
+
+extern const struct gfx_ops diff_ops;
+
+#endif /* !DIFF_H */
diff --git a/dwg.c b/dwg.c
new file mode 100644
index 0000000..08fa64a
--- /dev/null
+++ b/dwg.c
@@ -0,0 +1,463 @@
+/*
+ * dwg.c - Complex drawing functions
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#define _GNU_SOURCE /* for asprintf */
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "util.h"
+#include "misc.h"
+#include "style.h"
+#include "text.h"
+#include "gfx.h"
+#include "dwg.h"
+
+
+/* ----- Labels ------------------------------------------------------------ */
+
+
+enum box_type { // ___
+ box_simple, // [___]
+ box_left, // <___]
+ box_right, // [___>
+ box_both, // <___>
+};
+
+
+static enum box_type flip_box(enum box_type box)
+{
+ switch (box) {
+ case box_simple:
+ return box_simple;
+ case box_left:
+ return box_right;
+ case box_right:
+ return box_left;
+ case box_both:
+ return box_both;
+ default:
+ abort();
+ }
+}
+
+
+void dwg_label(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape)
+{
+ struct text txt = {
+ .s = s,
+ .size = dim,
+ .x = x,
+ .y = y,
+ .rot = 0,
+ .hor = 0,
+ .vert = text_min,
+ };
+ int dx = 0, dy = 0;
+
+ switch (dir) {
+ case 0: /* right */
+ txt.rot = 0;
+ txt.hor = text_min;
+ dy = 1;
+ break;
+ case 1: /* up */
+ txt.rot = 90;
+ txt.hor = text_min;
+ dx = -1;
+ break;
+ case 2: /* left */
+ txt.rot = 0;
+ txt.hor = text_max;
+ dy = 1;
+ break;
+ case 3: /* down */
+ txt.rot = 90;
+ txt.hor = text_max;
+ dx = -1;
+ break;
+ default:
+ assert(0);
+ }
+
+ txt.y -= dy * LABEL_OFFSET;
+ txt.x += dx * LABEL_OFFSET;
+ text_fig(&txt, COLOR_LABEL, LAYER_LABEL);
+}
+
+
+void dwg_glabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape)
+{
+ struct text txt = {
+ .s = s,
+ .size = dim,
+ .x = x,
+ .y = y,
+ .rot = 0,
+ .hor = 0,
+ .vert = text_mid,
+ };
+ int n = 6;
+ int vx[7];
+ int vy[7];
+ int half = (dim >> 1) + GLABEL_OFFSET;
+ enum box_type box;
+ int dx, shift_flat, shift_tip;
+ bool anchor_right = 1;
+ char *tag;
+
+ switch (shape) {
+ case dwg_unspec:
+ box = box_simple;
+ break;
+ case dwg_in:
+ box = box_right;
+ break;
+ case dwg_out:
+ box = box_left;
+ break;
+ case dwg_bidir:
+ box = box_both;
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (dir) {
+ case 0: /* left */
+ txt.rot = 0;
+ txt.hor = text_max;
+ dx = -1;
+ break;
+ case 1: /* up */
+ txt.rot = 90;
+ txt.hor = text_min;
+ dx = 1;
+ box = flip_box(box);
+ anchor_right = !anchor_right;
+ break;
+ case 2: /* right */
+ txt.rot = 0;
+ txt.hor = text_min;
+ dx = 1;
+ box = flip_box(box);
+ anchor_right = !anchor_right;
+ break;
+ case 3: /* down */
+ txt.rot = 90;
+ txt.hor = text_max;
+ dx = -1;
+ break;
+ default:
+ assert(0);
+ }
+
+ shift_flat = dx * GLABEL_OFFSET;
+ shift_tip = dx * (GLABEL_OFFSET + half);
+
+ switch (box) {
+ case box_simple:
+ n = 5;
+ text_shift(&txt, txt.hor, text_mid, shift_flat, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ break;
+ case box_right:
+ text_shift(&txt, txt.hor, text_mid,
+ anchor_right ? shift_tip : shift_flat, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0,
+ vx + 3, vy + 3);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5);
+ break;
+ case box_left:
+ text_shift(&txt, txt.hor, text_mid,
+ anchor_right ? shift_flat : shift_tip, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 3, vy + 3);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0,
+ vx + 5, vy + 5);
+ break;
+ case box_both:
+ n = 7;
+ text_shift(&txt, txt.hor, text_mid, shift_tip, 0);
+ text_rel(&txt, text_min, text_min,
+ -GLABEL_OFFSET, GLABEL_OFFSET, vx + 1, vy + 1);
+ text_rel(&txt, text_max, text_min,
+ GLABEL_OFFSET, GLABEL_OFFSET, vx + 2, vy + 2);
+ text_rel(&txt, text_max, text_mid, GLABEL_OFFSET + half, 0,
+ vx + 3, vy + 3);
+ text_rel(&txt, text_max, text_max,
+ GLABEL_OFFSET, -GLABEL_OFFSET, vx + 4, vy + 4);
+ text_rel(&txt, text_min, text_max,
+ -GLABEL_OFFSET, -GLABEL_OFFSET, vx + 5, vy + 5);
+ text_rel(&txt, text_min, text_mid, -GLABEL_OFFSET- half, 0,
+ vx + 6, vy + 6);
+ break;
+ default:
+ assert(0);
+ }
+
+ text_fig(&txt, COLOR_GLABEL, LAYER_GLABEL);
+
+ vx[0] = vx[n - 1];
+ vy[0] = vy[n - 1];
+ gfx_poly(n, vx, vy, COLOR_GLABEL, COLOR_NONE, LAYER_GLABEL);
+
+ if (asprintf(&tag, "G:%s", s)) {}
+ gfx_tag(tag, n, vx, vy);
+}
+
+
+static int make_box(enum box_type box, int h, int *vx, int *vy)
+{
+ int r = h / 2;
+
+ switch (box) {
+ case box_simple:
+ vx[0] = 0;
+ vy[0] = -r;
+ vx[1] = 2 * r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = r;
+ vx[3] = 0;
+ vy[3] = r;
+ return 4;
+ case box_right:
+ vx[0] = 0;
+ vy[0] = -r;
+ vx[1] = r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = 0;
+ vx[3] = r;
+ vy[3] = r;
+ vx[4] = 0;
+ vy[4] = r;
+ return 5;
+ case box_left:
+ vx[0] = r;
+ vy[0] = -r;
+ vx[1] = 2 * r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = r;
+ vx[3] = r;
+ vy[3] = r;
+ vx[4] = 0;
+ vy[4] = 0;
+ return 5;
+ case box_both:
+ vx[0] = 0;
+ vy[0] = 0;
+ vx[1] = r;
+ vy[1] = -r;
+ vx[2] = 2 * r;
+ vy[2] = 0;
+ vx[3] = r;
+ vy[3] = r;
+ return 4;
+ default:
+ assert(0);
+ }
+}
+
+
+void dwg_hlabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape)
+{
+ struct text txt = {
+ .s = s,
+ .size = dim,
+ .x = x,
+ .y = y,
+ .rot = 0,
+ .hor = 0,
+ .vert = text_mid,
+ };
+ int vx[6], vy[6];
+ int rot;
+ int n, i;
+
+ switch (shape) {
+ case dwg_unspec:
+ n = make_box(box_simple, dim, vx, vy);
+ break;
+ case dwg_in:
+ n = make_box(box_left, dim, vx, vy);
+ break;
+ case dwg_out:
+ n = make_box(box_right, dim, vx, vy);
+ break;
+ case dwg_bidir:
+ n = make_box(box_both, dim, vx, vy);
+ break;
+ default:
+ assert(0);
+ }
+
+ switch (dir) {
+ case 0: /* right */
+ rot = 180;
+ txt.hor = text_max;
+ break;
+ case 1: /* up */
+ rot = 90;
+ txt.hor = text_min;
+ break;
+ case 2: /* left */
+ rot = 0;
+ txt.hor = text_min;
+ break;
+ case 3: /* down */
+ rot = 270;
+ txt.hor = text_max;
+ break;
+ default:
+ assert(0);
+ }
+
+ txt.x += rx((1 + HLABEL_OFFSET_F) * dim, 0, rot);
+ txt.y += ry((1 + HLABEL_OFFSET_F) * dim, 0, rot);
+
+ for (i = 0; i != n; i++) {
+ int tmp;
+
+ tmp = x + rx(vx[i], vy[i], rot);
+ vy[i] = y + ry(vx[i], vy[i], rot);
+ vx[i] = tmp;
+ }
+
+ vx[n] = vx[0];
+ vy[n] = vy[0];
+
+ txt.rot = rot % 180;
+
+ text_fig(&txt, COLOR_HLABEL, LAYER_HLABEL);
+ gfx_poly(n + 1, vx, vy, COLOR_HLABEL, COLOR_NONE, LAYER_HLABEL);
+}
+
+
+/* ----- Text -------------------------------------------------------------- */
+
+
+void dwg_text(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape)
+{
+ struct text txt = {
+ .s = s,
+ .size = dim,
+ .x = x,
+ .y = y,
+ .rot = 0,
+ .hor = text_min,
+ .vert = text_min,
+ };
+
+ switch (dir) {
+ case 0: /* right */
+ break;
+ case 1: /* up */
+ text_rot(&txt, 90);
+ break;
+ case 2: /* left */
+ txt.hor = text_max;
+ break;
+ case 3: /* down */
+ text_rot(&txt, 90);
+ txt.hor = text_max;
+ break;
+ default:
+ assert(2 + 2 == 5);
+ }
+
+ text_fig(&txt, COLOR_TEXT, LAYER_TEXT);
+}
+
+
+/* ----- Connections ------------------------------------------------------- */
+
+
+void dwg_junction(int x, int y)
+{
+ gfx_circ(x, y, JUNCTION_R, COLOR_NONE, COLOR_WIRE, LAYER_WIRES);
+}
+
+
+void dwg_noconn(int x, int y)
+{
+ int vx[2] = { x - NOCONN_LEN, x + NOCONN_LEN };
+ int vy[2] = { y - NOCONN_LEN, y + NOCONN_LEN };
+
+ gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN);
+ swap(vy[0], vy[1]);
+ gfx_poly(2, vx, vy, COLOR_NOCONN, COLOR_NONE, LAYER_NOCONN);
+}
+
+
+/* ----- Lines ------------------------------------------------------------- */
+
+/*
+ * We can't use gfx_poly because lines are dashed and we don't have that
+ * property at the gfx_poly API.
+ */
+
+void dwg_line(int sx, int sy, int ex, int ey)
+{
+ gfx_line(sx, sy, ex, ey, COLOR_SHEET_DWG, LAYER_LINES);
+}
+
+
+/* ----- Wires and busses -------------------------------------------------- */
+
+
+void dwg_wire(int sx, int sy, int ex, int ey)
+{
+ int vx[] = { sx, ex };
+ int vy[] = { sy, ey };
+
+ // WIDTH_WIRE
+ gfx_poly(2, vx, vy, COLOR_WIRE, COLOR_NONE, LAYER_WIRES);
+}
+
+
+void dwg_bus(int sx, int sy, int ex, int ey)
+{
+ int vx[] = { sx, ex };
+ int vy[] = { sy, ey };
+
+ // WIDTH_BUS
+ gfx_poly(2, vx, vy, COLOR_BUS, COLOR_NONE, LAYER_BUSSES);
+}
diff --git a/dwg.h b/dwg.h
new file mode 100644
index 0000000..697f524
--- /dev/null
+++ b/dwg.h
@@ -0,0 +1,46 @@
+/*
+ * dwg.h - Complex drawing functions
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#ifndef DWG_H
+#define DWG_H
+
+#include "fig.h"
+
+
+enum dwg_shape {
+ dwg_unspec, // UnSpc
+ dwg_in, // Input
+ dwg_out, // Output
+ dwg_tri, // 3State
+ dwg_bidir, // Bidirectional
+};
+
+
+void dwg_label(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape);
+void dwg_hlabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape);
+void dwg_glabel(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape);
+void dwg_text(int x, int y, const char *s, int dir, int dim,
+ enum dwg_shape shape);
+
+void dwg_junction(int x, int y);
+void dwg_noconn(int x, int y);
+
+void dwg_line(int sx, int sy, int ex, int ey);
+
+void dwg_wire(int sx, int sy, int ex, int ey);
+void dwg_bus(int sx, int sy, int ex, int ey);
+
+#endif /* !DWG_H */
diff --git a/fig.c b/fig.c
new file mode 100644
index 0000000..04ec9f4
--- /dev/null
+++ b/fig.c
@@ -0,0 +1,293 @@
+/*
+ * fig.c - Generate FIG output for Eeschema items
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include "util.h"
+#include "style.h"
+#include "text.h"
+#include "main.h"
+#include "fig.h"
+
+
+/*
+ * FIG works with 1/1200 in
+ * KiCad works with mil
+ * 1 point = 1/72 in
+ */
+
+
+static inline int cx(int x)
+{
+ return x * 1200 / 1000;
+}
+
+
+static inline int cy(int y)
+{
+ return y * 1200 / 1000;
+}
+
+
+static inline float pt(int x)
+{
+ return cx(x) * 72 * 1.5 / 1200.0;
+}
+
+
+/* ----- Schematics items -------------------------------------------------- */
+
+
+static void fig_line(void *ctx, int sx, int sy, int ex, int ey,
+ int color, unsigned layer)
+{
+ // TypeStyle FillCol AreaFil Cap FwdAr
+ // SubTy Color Pen StyleV Rad BwdAr
+ // Thick Depth Join Points
+ printf("2 1 2 %d %d 7 %d -1 -1 3.0 1 1 -1 0 0 2\n",
+ WIDTH_LINE, color, layer);
+ printf("\t%d %d %d %d\n", cx(sx), cy(sy), cx(ex), cy(ey));
+}
+
+
+/* ----- General items ----------------------------------------------------- */
+
+
+static void fig_rect(void *ctx, int sx, int sy, int ex, int ey,
+ int color, int fill_color, unsigned layer)
+{
+ // Type Thick Depth StyleV Rad
+ // SubTy Color Pen Join FwdAr
+ // Style FillCol AreaFil Cap BwdAr
+ printf("2 2 0 %d %d %d %d -1 %d 0.0 1 1 -1 0 0 5\n",
+ color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer,
+ fill_color == -1 ? -1 : 20);
+ printf("\t%d %d %d %d %d %d %d %d %d %d\n",
+ cx(sx), cy(sy), cx(ex), cy(sy), cx(ex), cy(ey), cx(sx), cy(ey),
+ cx(sx), cy(sy));
+}
+
+
+static void fig_poly(void *ctx,
+ int points, const int x[points], const int y[points],
+ int color, int fill_color, unsigned layer)
+{
+ int i;
+ char ch = '\t';
+
+ // Type Thick Depth StyleV Rad
+ // SubTy Color Pen Join FwdAr
+ // Style FillCol AreaFil Cap BwdAr
+ printf("2 1 0 %d %d %d %d -1 %d 0.0 1 1 -1 0 0 %d\n",
+ color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer,
+ fill_color == -1 ? -1 : 20, points);
+ for (i = 0; i != points; i++) {
+ printf("%c%d %d", ch, cx(x[i]), cy(y[i]));
+ ch = ' ';
+ }
+ printf("\n");
+}
+
+
+static void fig_circ(void *ctx, int x, int y, int r,
+ int color, int fill_color, unsigned layer)
+{
+ // Type Thick Depth StyleV Cx Rx Sx Ex
+ // SubTy Color Pen Dir Cy Ry Sy Ey
+ // Style FillCol AreaFil Angle
+ printf("1 3 0 %d %d %d %d -1 %d 0.0 1 0.0 %d %d %d %d %d %d %d %d\n",
+ color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer,
+ fill_color == -1 ? -1 : 20,
+ cx(x), cy(y), r, r,
+ cx(x), cy(y), cx(x) + r, cy(y));
+}
+
+
+static int ax(int x, int y, int r, int angle)
+{
+ float a = angle / 180.0 * M_PI;
+
+ return cx(x + r * cos(a));
+}
+
+
+static int ay(int x, int y, int r, int angle)
+{
+ float a = angle / 180.0 * M_PI;
+
+ return cy(y - r * sin(a));
+}
+
+
+static void fig_arc(void *ctx, int x, int y, int r, int sa, int ea,
+ int color, int fill_color, unsigned layer)
+{
+ int ma = (sa + ea) / 2;
+
+ // Type Thick Depth StyleV FwdAr
+ // SubTy Color Pen Cap BwdAr
+ // Style FillCol AreaFil Dir points
+ printf("5 1 0 %d %d %d %d -1 %d 0.0 1 1 0 0 %d %d %d %d %d %d %d %d\n",
+ color == -1 ? 0 : WIDTH_COMP_DWG, color, fill_color, layer,
+ fill_color == -1 ? -1 : 20,
+ cx(x), cy(y),
+ ax(x, y, r, sa), ay(x, y, r, sa),
+ ax(x, y, r, ma), ay(x, y, r, ma),
+ ax(x, y, r, ea), ay(x, y, r, ea));
+}
+
+
+static void fig_tag(void *ctx, const char *s,
+ int points, const int x[points], const int y[points])
+{
+ printf("# href=\"%s\" alt=\"\"\n", s);
+ fig_poly(ctx, points, x, y, COLOR_NONE, COLOR_NONE, 999);
+}
+
+
+static void fig_text(void *ctx, int x, int y, const char *s, unsigned size,
+ enum text_align align, int rot, unsigned color, unsigned layer)
+{
+ // Type Depth FontSiz Height
+ // Just Pen Angle Length
+ // Color Font Flags X Y
+ printf("4 %u %d %d -1 %d %f %f 4 0.0 0.0 %d %d %s\\001\n",
+ align, color, layer, FONT_HELVETICA_BOLD,
+ pt(size), rot / 180.0 * M_PI, cx(x), cy(y), s);
+}
+
+
+static unsigned fig_text_width(void *ctx, const char *s, unsigned size)
+{
+ /*
+ * Note that we stretch the text size, so the ratio is larger than
+ * expressed here.
+ */
+ return strlen(s) * size * 1.0;
+}
+
+
+/* ----- FIG file header --------------------------------------------------- */
+
+
+static void fig_header(void)
+{
+ printf("#FIG 3.2\n");
+ printf("Landscape\n");
+ printf("Center\n");
+ printf("Metric\n");
+ printf("A4\n");
+ printf("100.00\n");
+ printf("Single\n");
+ printf("-2\n");
+ printf("1200 2\n");
+
+ /* User32, COLOR_DARK_YELLOW */
+ printf("0 32 #848400\n");
+}
+
+
+static bool apply_vars(char *buf, int n_vars, const char **vars)
+{
+ char *p;
+ const char **var, *eq;
+ int var_len, value_len;
+
+ p = strchr(buf, '<');
+ if (!p)
+ return 0;
+ for (var = vars; var != vars + n_vars; var++) {
+ eq = strchr(*var, '=');
+ assert(eq);
+ var_len = eq - *var;
+ if (strncmp(p + 1, *var, var_len))
+ continue;
+ value_len = strlen(eq + 1);
+ memmove(p + value_len, p + var_len + 2,
+ strlen(p + var_len + 2) + 1);
+ memcpy(p, eq + 1, value_len);
+ return 1;
+ }
+ return 0;
+}
+
+
+
+static void *fig_init(int argc, char *const *argv)
+{
+ const char *template = NULL;
+ const char **vars = NULL;
+ int n_vars = 0;
+ char c;
+ int arg;
+ FILE *file;
+ char buf[1000];
+
+ while ((c = getopt(argc, argv, "t:")) != EOF)
+ switch (c) {
+ case 't':
+ template = optarg;
+ break;
+ default:
+ usage(*argv);
+ }
+
+ for (arg = optind; arg != argc; arg++) {
+ if (!strchr(argv[arg], '='))
+ usage(*argv);
+ n_vars++;
+ vars = realloc(vars, sizeof(const char *) * n_vars);
+ vars[n_vars - 1] = argv[arg];
+ }
+
+ if (!template) {
+ fig_header();
+ return NULL;
+ }
+
+ file = fopen(template, "r");
+ if (!file) {
+ perror(template);
+ exit(1);
+ }
+ while (fgets(buf, sizeof(buf), file)) {
+ while (apply_vars(buf, n_vars, vars));
+ printf("%s", buf);
+ }
+ fclose(file);
+
+ return NULL;
+}
+
+
+/* ----- Operations -------------------------------------------------------- */
+
+
+const struct gfx_ops fig_ops = {
+ .name = "fig",
+ .line = fig_line,
+ .rect = fig_rect,
+ .poly = fig_poly,
+ .circ = fig_circ,
+ .arc = fig_arc,
+ .text = fig_text,
+ .tag = fig_tag,
+ .text_width = fig_text_width,
+ .init = fig_init,
+};
diff --git a/fig.h b/fig.h
new file mode 100644
index 0000000..6e6a361
--- /dev/null
+++ b/fig.h
@@ -0,0 +1,22 @@
+/*
+ * fig.h - Generate FIG output for Eeschema items
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#ifndef FIG_H
+#define FIG_H
+
+#include "gfx.h"
+
+
+extern const struct gfx_ops fig_ops;
+
+#endif /* !FIG_H */
diff --git a/file.c b/file.c
new file mode 100644
index 0000000..1145173
--- /dev/null
+++ b/file.c
@@ -0,0 +1,76 @@
+/*
+ * file.c - Open and read a file
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "util.h"
+#include "main.h"
+#include "git-file.h"
+#include "file.h"
+
+
+bool file_cat(void *user, const char *line)
+{
+ printf("%s\n", line);
+ return 1;
+}
+
+
+static void read_from_file(FILE *file,
+ bool (*parse)(void *user, const char *line), void *user)
+{
+ char buf[1000];
+ char *nl;
+
+ while (fgets(buf, sizeof(buf), file)) {
+ nl = strchr(buf, '\n');
+ if (nl)
+ *nl = 0;
+ if (!parse(user, buf))
+ break;
+ }
+}
+
+
+void file_read(const char *name, bool (*parse)(void *user, const char *line),
+ void *user)
+{
+ FILE *file;
+ char *colon, *tmp;
+
+ file = fopen(name, "r");
+ if (file) {
+ if (verbose)
+ fprintf(stderr, "reading %s\n", name);
+ read_from_file(file, parse, user);
+ fclose(file);
+ return;
+ }
+
+ if (verbose)
+ perror(name);
+
+ colon = strchr(name, ':');
+ if (!colon) {
+ if (!verbose)
+ perror(name);
+ exit(1);
+ }
+
+ tmp = stralloc(name);
+ tmp[colon - name] = 0;
+ git_read(tmp, colon + 1, parse, user);
+ free(tmp);
+}
diff --git a/file.h b/file.h
new file mode 100644
index 0000000..a6cc170
--- /dev/null
+++ b/file.h
@@ -0,0 +1,22 @@
+/*
+ * file.h - Open and read a file
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+#ifndef FILE_H
+#define FILE_H
+
+#include <stdbool.h>
+
+bool file_cat(void *user, const char *line);
+void file_read(const char *name, bool (*parse)(void *user, const char *line),
+ void *user);
+
+#endif /* !FILE_H */
diff --git a/gfx.c b/gfx.c
new file mode 100644
index 0000000..ba88134
--- /dev/null
+++ b/gfx.c
@@ -0,0 +1,120 @@
+/*
+ * gfx.c - Generate graphical output for Eeschema items
+ *
+ * Written 2016 by Werner Almesberger
+ * Copyright 2016 by Werner Almesberger
+ *
+ * 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.
+ */
+
+
+#include <stdbool.h>
+
+#include "style.h"
+#include "text.h"
+#include "gfx.h"
+
+
+void *gfx_ctx;
+
+static const struct gfx_ops *gfx_ops;
+
+
+void gfx_line(int sx, int sy, int ex, int ey, int color, unsigned layer)
+{
+ if (gfx_ops->line) {
+ gfx_ops->line(gfx_ctx, sx, sy, ex, ey, color, layer);
+ return;
+ }
+
+ int vx[] = { sx, ex };
+ int vy[] = { sy, ey };
+
+ gfx_poly(2, vx, vy, color, COLOR_NONE, layer);
+}
+
+
+void gfx_rect(int sx, int sy, int ex, int ey,
+ int color, int fill_color, unsigned layer)
+{
+ if (gfx_ops->rect) {
+ gfx_ops->rect(gfx_ctx, sx, sy, ex, ey,
+ color, fill_color, layer);
+ return;
+ }
+
+ int vx[] = { sx, ex, ex, sx, sx };
+ int vy[] = { sy, sy, ey, ey, sy };
+
+ gfx_poly(5, vx, vy, color, fill_color, layer);
+}
+
+
+void gfx_poly(int points, const int x[points], const int y[points],
+ int color, int fill_color, unsign