1
0
mirror of git://projects.qi-hardware.com/eda-tools.git synced 2024-11-08 00:51:54 +02:00
eda-tools/sch2fig/cairo.c

409 lines
7.8 KiB
C
Raw Normal View History

/*
* cairo.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.
*/
2016-08-01 07:34:27 +03:00
#include <stddef.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <math.h>
#include <cairo/cairo.h>
2016-08-01 06:37:32 +03:00
#include <cairo/cairo-pdf.h>
#include "util.h"
#include "style.h"
2016-07-31 09:17:10 +03:00
#include "text.h"
#include "gfx.h"
#include "layer.h"
#include "main.h"
#include "cairo.h"
/*
* FIG works with 1/1200 in
* KiCad works with mil
* 1 point = 1/72 in
*/
#define DEFAULT_SCALE (72.0 / 1200)
struct cairo_ctx {
struct layer layer; /* must be first */
int xo, yo;
float scale;
cairo_t *cr;
cairo_surface_t *s;
2016-08-01 07:34:27 +03:00
struct layer *sheets; /* for PDF */
unsigned n_sheets;
const char *output_name;
};
static inline int cd(struct cairo_ctx *cc, int x)
{
return x * cc->scale;
}
static inline int cx(struct cairo_ctx *cc, int x)
{
return cc->xo + x * cc->scale;
}
static inline int xc(struct cairo_ctx *cc, int x)
{
return (x - cc->xo) / cc->scale;
}
static inline int cy(struct cairo_ctx *cc, int y)
{
return cc->yo + y * cc->scale;
}
static inline float pt(struct cairo_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_poly(void *ctx, int points, int x[points], int y[points],
int color, int fill_color, unsigned layer)
{
struct cairo_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 cairo_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 cairo_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)
{
2016-07-31 09:17:10 +03:00
struct cairo_ctx *cc = ctx;
cairo_text_extents_t ext;
cairo_matrix_t m;
cairo_set_font_size(cc->cr, cd(cc, size) * TEXT_STRETCH);
2016-07-31 09:17:10 +03:00
cairo_text_extents(cc->cr, s, &ext);
set_color(cc->cr, color);
cairo_move_to(cc->cr, cx(cc, x), cy(cc, y));
2016-07-31 09:17:10 +03:00
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 cairo_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);
}
/* ----- Initializatio and termination ------------------------------------- */
static const struct gfx_ops real_cairo_ops = {
.name = "cairo",
// .line = cr_line, @@@ later
.poly = cr_poly,
.circ = cr_circ,
.arc = cr_arc,
.text = cr_text,
.text_width = cr_text_width,
};
2016-08-01 06:37:32 +03:00
static struct cairo_ctx *init_common(int argc, char *const *argv)
{
struct cairo_ctx *cc;
char c;
cc = alloc_type(struct cairo_ctx);
cc->xo = cc->yo = 0;
cc->scale = DEFAULT_SCALE;
2016-08-01 07:34:27 +03:00
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);
}
layer_init(&cc->layer, &real_cairo_ops, cc);
2016-08-01 06:37:32 +03:00
return cc;
}
static void *cr_png_init(int argc, char *const *argv)
{
struct cairo_ctx *cc;
cc = init_common(argc, argv);
/* cr_text_width needs *something* to work with */
2016-07-31 09:17:10 +03:00
cc->s = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 16, 16);
cc->cr = cairo_create(cc->s);
return cc;
}
2016-08-01 06:37:32 +03:00
static void *cr_pdf_init(int argc, char *const *argv)
{
2016-08-01 06:37:32 +03:00
struct cairo_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 cairo_ctx *cc, int *w, int *h)
{
int x, y;
cairo_surface_destroy(cc->s);
cairo_destroy(cc->cr);
2016-08-01 06:37:32 +03:00
layer_bbox(&cc->layer, &x, &y, w, h);
2016-08-01 06:37:32 +03:00
// fprintf(stderr, "%dx%d%+d%+d\n", *w, *h, x, y);
cc->xo = -cd(cc, x);
cc->yo = -cd(cc, y);
2016-08-01 06:37:32 +03:00
*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 cairo_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, 3);
layer_replay(&cc->layer);
layer_destroy(&cc->layer);
if (cc->output_name)
cairo_surface_write_to_png(cc->s, cc->output_name);
}
2016-08-01 07:34:27 +03:00
static void cr_pdf_new_sheet(void *ctx)
{
struct cairo_ctx *cc = ctx;
cc->n_sheets++;
cc->sheets = realloc(cc->sheets, sizeof(struct layer) * cc->n_sheets);
if (!cc->sheets) {
perror("realloc");
exit(1);
}
cc->sheets[cc->n_sheets - 1] = cc->layer;
layer_wipe(&cc->layer);
}
2016-08-01 06:37:32 +03:00
static void cr_pdf_end(void *ctx)
{
struct cairo_ctx *cc = ctx;
int w, h;
2016-08-01 07:34:27 +03:00
unsigned i;
2016-08-01 06:37:32 +03:00
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, 3);
2016-08-01 07:34:27 +03:00
for (i = 0; i != cc->n_sheets; i++) {
set_color(cc->cr, COLOR_WHITE);
cairo_paint(cc->cr);
layer_replay(cc->sheets + i);
layer_destroy(cc->sheets + i);
cairo_show_page(cc->cr);
}
2016-08-01 06:37:32 +03:00
layer_replay(&cc->layer);
layer_destroy(&cc->layer);
cairo_show_page(cc->cr);
cairo_surface_destroy(cc->s);
cairo_destroy(cc->cr);
}
/* ----- Operations -------------------------------------------------------- */
2016-08-01 06:37:32 +03:00
const struct gfx_ops cairo_png_ops = {
.name = "png",
// .line = cr_line, @@@ later
.poly = layer_poly,
.circ = layer_circ,
.arc = layer_arc,
.text = layer_text,
.text_width = cr_text_width,
2016-08-01 06:37:32 +03:00
.init = cr_png_init,
.end = cr_png_end,
};
const struct gfx_ops cairo_pdf_ops = {
.name = "pdf",
// .line = cr_line, @@@ later
.poly = layer_poly,
.circ = layer_circ,
.arc = layer_arc,
.text = layer_text,
.text_width = cr_text_width,
.init = cr_pdf_init,
2016-08-01 07:34:27 +03:00
.new_sheet = cr_pdf_new_sheet,
2016-08-01 06:37:32 +03:00
.end = cr_pdf_end,
};