mirror of
git://projects.qi-hardware.com/eda-tools.git
synced 2024-11-05 10:20:37 +02:00
463e8dcac4
Before, duplicate names in the tree always pointed to the same page. Now they point to the respective locations. Note that this does not affect handling of duplicate names in libraries, where still only one "wins" while the other is completely ignored.
356 lines
7.9 KiB
C
356 lines
7.9 KiB
C
/*
|
|
* pdf.c - Generate PDF
|
|
*
|
|
* Copyright 2012 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "util.h"
|
|
#include "gencat.h"
|
|
#include "tree.h"
|
|
#include "libs.h"
|
|
#include "pdf.h"
|
|
|
|
|
|
static struct format {
|
|
const char *file_setup;
|
|
const char *overlay_setup;
|
|
const char *comp_setup;
|
|
int left;
|
|
struct text {
|
|
const char *font;
|
|
int size;
|
|
int y;
|
|
} name, path, lib, comment, index;
|
|
int comment_line_skip;
|
|
int index_line_skip;
|
|
int index_column_skip;
|
|
int index_lines;
|
|
int index_columns;
|
|
} landscape = {
|
|
.file_setup = "%%Orientation: Landscape",
|
|
.overlay_setup = "90 rotate",
|
|
.comp_setup = "",
|
|
.left = 20,
|
|
.name = { "Helvetica-Bold", 24, 57 },
|
|
.path = { "Helvetica-Bold", 18, 30 },
|
|
.lib = { "Courier", 12, 75 },
|
|
.comment = { "Helvetica", 12, 600 },
|
|
.index = { "Helvetica", 10, 32 },
|
|
.comment_line_skip = 14,
|
|
.index_line_skip = 12,
|
|
.index_column_skip = 140,
|
|
.index_lines = 45,
|
|
.index_columns = 6,
|
|
}, portrait = {
|
|
.file_setup = "%%Orientation: Portrait",
|
|
.overlay_setup = "0 790 translate",
|
|
.comp_setup = "-120 700 translate -90 rotate",
|
|
.left = 20,
|
|
.name = { "Helvetica-Bold", 24, 57 },
|
|
.path = { "Helvetica-Bold", 18, 30 },
|
|
.lib = { "Courier", 12, 75 },
|
|
.comment = { "Helvetica", 12, 740 },
|
|
.index = { "Helvetica", 10, 0 },
|
|
.comment_line_skip = 14,
|
|
.index_line_skip = 12,
|
|
.index_column_skip = 140,
|
|
.index_lines = 63,
|
|
.index_columns = 4,
|
|
}, format;
|
|
|
|
static int total, done = 0;
|
|
|
|
|
|
/* ----- Utility functions ------------------------------------------------- */
|
|
|
|
|
|
static void ps_string(FILE *file, const char *s)
|
|
{
|
|
fputc('(', file);
|
|
while (*s) {
|
|
if (*s == '(' || *s == ')' || *s == '\\')
|
|
fputc('\\', file);
|
|
fputc(*s, file);
|
|
s++;
|
|
}
|
|
fputc(')', file);
|
|
}
|
|
|
|
|
|
/* ----- Alphabetic index -------------------------------------------------- */
|
|
|
|
|
|
struct index {
|
|
const char *s;
|
|
const struct node *node;
|
|
};
|
|
|
|
|
|
static void collect_names(const struct node *node, struct index **idx, int *n)
|
|
{
|
|
const struct name *name;
|
|
|
|
while (node) {
|
|
if (node->child)
|
|
collect_names(node->child, idx, n);
|
|
else {
|
|
for (name = node->e->names; name; name = name->next) {
|
|
(*n)++;
|
|
*idx = realloc(*idx, *n*sizeof(struct entry));
|
|
if (!*idx)
|
|
abort();
|
|
(*idx)[*n-1].s = name->s;
|
|
(*idx)[*n-1].node = node;
|
|
}
|
|
}
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
|
|
static int comp(const void *a, const void *b)
|
|
{
|
|
const struct index *ai = a;
|
|
const struct index *bi = b;
|
|
|
|
return strcmp(ai->s, bi->s);
|
|
}
|
|
|
|
|
|
static void make_index(FILE *file, const struct node *node)
|
|
{
|
|
struct index *idx = NULL;
|
|
const struct index *p;
|
|
int n = 0;
|
|
int line = 0, col = 0;
|
|
|
|
collect_names(node, &idx, &n);
|
|
qsort(idx, n, sizeof(struct index), comp);
|
|
|
|
fprintf(file, "[ /Title (Index) /Count 0 /OUT pdfmark\n");
|
|
|
|
fprintf(file, "/%s findfont %d scalefont setfont\n",
|
|
format.index.font, format.index.size);
|
|
fprintf(file, "gsave %s 0 setgray\n", format.overlay_setup);
|
|
|
|
for (p = idx; p != idx+n; p++) {
|
|
if (line == format.index_lines) {
|
|
line = 0;
|
|
col++;
|
|
}
|
|
if (col == format.index_columns) {
|
|
fprintf(file, "grestore showpage\n");
|
|
fprintf(file, "gsave %s 0 setgray\n",
|
|
format.overlay_setup);
|
|
col = 0;
|
|
}
|
|
fprintf(file, "newpath %d -%d moveto currentpoint\n",
|
|
format.left+col*format.index_column_skip,
|
|
format.index.y+line*format.index_line_skip);
|
|
|
|
fprintf(file, "[ /Rect [ ");
|
|
ps_string(file, p->s);
|
|
fprintf(file, " false charpath flattenpath pathbbox ]\n");
|
|
fprintf(file, " /Subtype /Link\n");
|
|
fprintf(file, " /Border [ 0 0 0 ]\n");
|
|
fprintf(file, " /Action << /Subtype /GoTo /Dest /%p%p >>\n",
|
|
p->node, p->s);
|
|
fprintf(file, " /ANN pdfmark\n");
|
|
|
|
fprintf(file, "moveto ");
|
|
ps_string(file, p->s);
|
|
fprintf(file, " show\n");
|
|
line++;
|
|
}
|
|
fprintf(file, "grestore showpage\n");
|
|
|
|
free(idx);
|
|
}
|
|
|
|
|
|
/* ----- Overlay and table of contents ------------------------------------- */
|
|
|
|
|
|
static int children(const struct node *node)
|
|
{
|
|
int n = 0;
|
|
|
|
while (node) {
|
|
n++;
|
|
node = node->next;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
|
|
static void print_path(FILE *file, const struct node *node)
|
|
{
|
|
if (node->parent) {
|
|
print_path(file, node->parent);
|
|
fprintf(file, "( > ) show\n");
|
|
}
|
|
ps_string(file, node->name);
|
|
fprintf(file, " 0.5 setgray show 0 setgray\n");
|
|
}
|
|
|
|
|
|
static void make_title(FILE *file, const struct node *node, int unit)
|
|
{
|
|
const struct name *name;
|
|
|
|
fprintf(file, "gsave %s 0 setgray\n", format.overlay_setup);
|
|
|
|
fprintf(file, "/%s findfont %d scalefont setfont\n",
|
|
format.name.font, format.name.size);
|
|
fprintf(file, "%d %d moveto\n", format.left, -format.name.y);
|
|
for (name = node->e->names; name; name = name->next) {
|
|
if (name != node->e->names)
|
|
fprintf(file, "(, ) show 0.5 setgray\n");
|
|
ps_string(file, name->s);
|
|
fprintf(file, " show\n");
|
|
if (!unit)
|
|
fprintf(file, "[ /Dest /%p%p /DEST pdfmark\n",
|
|
node, name->s);
|
|
}
|
|
fprintf(file, "0 setgray\n");
|
|
if (node->e->units > 1)
|
|
fprintf(file, " ( \\(%c\\)) show\n", 'A'+unit);
|
|
|
|
fprintf(file, "/%s findfont %d scalefont setfont\n",
|
|
format.path.font, format.path.size);
|
|
fprintf(file, "%d %d moveto\n", format.left, -format.path.y);
|
|
print_path(file, node);
|
|
|
|
fprintf(file, "/%s findfont %d scalefont setfont\n",
|
|
format.lib.font, format.lib.size);
|
|
fprintf(file, "%d %d moveto\n", format.left, -format.lib.y);
|
|
ps_string(file, node->e->file->path);
|
|
fprintf(file, " show\n");
|
|
|
|
fprintf(file, "grestore\n");
|
|
}
|
|
|
|
|
|
static void print_comment(FILE *file, const struct line *comment)
|
|
{
|
|
const struct line *line;
|
|
int lines = 0;
|
|
int n;
|
|
|
|
fprintf(file, "gsave %s 0 setgray\n", format.overlay_setup);
|
|
fprintf(file, "/%s findfont %d scalefont setfont\n",
|
|
format.comment.font, format.comment.size);
|
|
for (line = comment; line; line = line->next)
|
|
lines++;
|
|
n = 0;
|
|
for (line = comment; line; line = line->next) {
|
|
n++;
|
|
fprintf(file, "%d -%d moveto\n", format.left,
|
|
format.comment.y+(n-lines)*format.comment_line_skip);
|
|
ps_string(file, line->s);
|
|
fprintf(file, " show\n");
|
|
}
|
|
fprintf(file, "grestore\n");
|
|
}
|
|
|
|
|
|
/* ----- Component conversion ---------------------------------------------- */
|
|
|
|
|
|
static void convert_comp(const struct node *node, FILE *out)
|
|
{
|
|
const struct lib *lib = node->e->file->lib;
|
|
int i;
|
|
|
|
for (i = 0; i != node->e->units; i++) {
|
|
if (!quiet) {
|
|
fprintf(stderr, "\r%u/%u", ++done, total);
|
|
fflush(stderr);
|
|
}
|
|
make_title(out, node, i);
|
|
if (!i && node->comment)
|
|
print_comment(out, node->comment);
|
|
fprintf(out, "gsave %s\n", format.comp_setup);
|
|
node->e->file->lib->ps_entry(out, lib, node->e, i);
|
|
fprintf(out, "\ngrestore\n");
|
|
}
|
|
}
|
|
|
|
|
|
static void convert_tree(const struct node *node, FILE *out)
|
|
{
|
|
while (node) {
|
|
fprintf(out, "[ /Title (%s) /Count %d /OUT pdfmark\n",
|
|
node->name, -children(node->child));
|
|
if (node->child)
|
|
convert_tree(node->child, out);
|
|
else
|
|
convert_comp(node, out);
|
|
node = node->next;
|
|
}
|
|
}
|
|
|
|
|
|
/* ----- Setup and PDF generation ------------------------------------------ */
|
|
|
|
|
|
static int count_tree(const struct node *node)
|
|
{
|
|
int sum = 0;
|
|
|
|
while (node) {
|
|
if (node->child)
|
|
sum += count_tree(node->child);
|
|
else
|
|
sum += node->e->units;
|
|
node = node->next;
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
|
|
void make_pdf(int pdf, int use_portrait)
|
|
{
|
|
FILE *out;
|
|
int res;
|
|
|
|
if (use_portrait)
|
|
format = portrait;
|
|
else
|
|
format = landscape;
|
|
if (pdf)
|
|
out = popen(
|
|
"gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=- "
|
|
"-f -", "w");
|
|
else
|
|
out = popen("cat", "w");
|
|
if (!out) {
|
|
perror("gs");
|
|
exit(1);
|
|
}
|
|
fprintf(out, "%%!PS\n%s\n", format.file_setup);
|
|
make_index(out, tree);
|
|
total = count_tree(tree);
|
|
convert_tree(tree, out);
|
|
if (!quiet)
|
|
fprintf(stderr, "\rFinishing\n");
|
|
res = pclose(out);
|
|
if (res < 0) {
|
|
perror("pclose");
|
|
exit(1);
|
|
}
|
|
if (res) {
|
|
fprintf(stderr, "exit status %d\n", res);
|
|
exit(1);
|
|
}
|
|
}
|