summaryrefslogtreecommitdiff
path: root/dviware/catdvi/canvas.c
diff options
context:
space:
mode:
Diffstat (limited to 'dviware/catdvi/canvas.c')
-rw-r--r--dviware/catdvi/canvas.c368
1 files changed, 368 insertions, 0 deletions
diff --git a/dviware/catdvi/canvas.c b/dviware/catdvi/canvas.c
new file mode 100644
index 0000000000..f4d1186aa7
--- /dev/null
+++ b/dviware/catdvi/canvas.c
@@ -0,0 +1,368 @@
+/* catdvi - get text from DVI files
+ Copyright (C) 2002 Bjoern Brill <brill@fs.math.uni-frankfurt.de>
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdlib.h>
+#include <assert.h>
+#include "util.h"
+#include "canvas.h"
+#include "glyphenm.h"
+#include "outenc.h"
+
+
+typedef enum canv_celltype_t {
+ CCT_UNUSED,
+ CCT_BLACK,
+ CCT_LINEDRAW,
+ CCT_LINEBUF_CONT,
+ CCT_GLYPH,
+ CCT_LINEBUF_START
+} canv_celltype_t;
+ /* The order matters. */
+
+typedef enum canv_linedraw_t {
+ CLD_RIGHT = 1 << 1,
+ CLD_UP = 1 << 2,
+ CLD_LEFT = 1 << 3,
+ CLD_DOWN = 1 << 4
+} canv_linedraw_t;
+
+typedef union canv_celldata_t {
+ /* */ /* CCT_UNUSED */
+ /* */ /* CCT_BLACK */
+ canv_linedraw_t draw; /* CCT_LINEDRAW */
+ glyph_t glyph; /* CCT_GLYPH */
+ linebuf_t * lb; /* CCT_LINEBUF_START, CCT_LINEBUF_CONT */
+} canv_celldata_t;
+
+struct canv_cell_t {
+ canv_celltype_t type;
+ canv_celldata_t data;
+};
+
+struct canv_line_t {
+ canv_cell_t * cells;
+ sint32 cols_used;
+};
+
+#define canv_cell(this, x, y) ((this)->lines[(y)].cells + (x))
+
+#define CANV_CHECK_X(this, x) {assert(0 <= (x)); assert((x) < (this)->ncols);}
+#define CANV_CHECK_Y(this, y) {assert(0 <= (y)); assert((y) < (this)->nlines);}
+
+
+/* Claim cell at (x, y) for use as celltype whatfor.
+ * Returns pointer to cell if succesful, NULL if cell already used
+ * for something equally or more important.
+ */
+static canv_cell_t * canv_allocate_cell(
+ canv_t * this,
+ sint32 x,
+ sint32 y,
+ canv_celltype_t whatfor
+);
+
+/* Add line drawings draw to cell at (x, y).
+ * Returns pointer to cell if succesful, NULL if cell already used
+ * for something more important.
+ */
+static canv_cell_t * canv_linedraw_to_cell(
+ canv_t * this,
+ sint32 x,
+ sint32 y,
+ canv_linedraw_t draw
+);
+
+
+void canv_init(canv_t * this, sint32 ncols, sint32 nlines)
+{
+ sint32 i, j;
+ canv_line_t * l;
+
+ assert(ncols > 0);
+ assert(nlines > 0);
+ this->ncols = ncols;
+ this->nlines = nlines;
+
+ this->cells = xmalloc(ncols * nlines * sizeof(canv_cell_t));
+ this->lines = xmalloc(nlines * sizeof(canv_line_t));
+
+ for(i = 0; i < nlines; ++i) {
+ l = this->lines + i;
+ l->cols_used = 0;
+ l->cells = this->cells + i * ncols;
+ for(j = 0; j < ncols; ++j) {
+ l->cells[j].type = CCT_UNUSED;
+ }
+ }
+}
+
+void canv_done(canv_t * this)
+{
+ sint32 i, j;
+ canv_cell_t * c;
+
+ for(i = 0; i < this->nlines; ++i) {
+ for(j = 0; j < this->ncols; ++j) {
+ c = canv_cell(this, j, i);
+ if(c->type == CCT_LINEBUF_START) {
+ linebuf_done(c->data.lb);
+ free(c->data.lb);
+ }
+ }
+ }
+
+ free(this->lines);
+ free(this->cells);
+}
+
+void canv_put_linebuf(
+ canv_t * this,
+ linebuf_t * lb,
+ sint32 xleft,
+ sint32 y,
+ sint32 width
+)
+{
+ canv_cell_t * cell;
+ sint32 x;
+
+ CANV_CHECK_X(this, xleft);
+ CANV_CHECK_Y(this, y);
+
+ if(width <= 0) {
+ /* This shouldn't happen, but is no reason to bomb out. */
+ linebuf_done(lb);
+ free(lb);
+ pmesg(
+ 10,
+ "Attempt to put <= 0 (%ld) width linebuf at (%ld, %ld) !\n",
+ width,
+ xleft,
+ y
+ );
+ return;
+ }
+
+ cell = canv_allocate_cell(this, xleft, y, CCT_LINEBUF_START);
+ if(cell == NULL) {
+ /* If we can't get it started, we just forget the whole thing. */
+ linebuf_done(lb);
+ free(lb);
+ return;
+ }
+ cell->data.lb = lb;
+
+ for(x = xleft + 1; x < xleft + width; ++x) {
+ cell = canv_allocate_cell(this, x, y, CCT_LINEBUF_CONT);
+ if(cell != NULL) cell->data.lb = lb;
+ }
+
+}
+
+void canv_put_glyph(canv_t * this, glyph_t glyph, sint32 x, sint32 y)
+{
+ canv_cell_t * cell;
+
+ CANV_CHECK_X(this, x);
+ CANV_CHECK_Y(this, y);
+
+ cell = canv_allocate_cell(this, x, y, CCT_GLYPH);
+ if(cell != NULL) cell->data.glyph = glyph;
+}
+
+void canv_put_hline(canv_t * this, sint32 xleft, sint32 y, sint32 xright)
+{
+ sint32 x;
+
+ CANV_CHECK_X(this, xleft);
+ CANV_CHECK_Y(this, y);
+ CANV_CHECK_X(this, xright);
+ assert(xleft <= xright);
+
+ canv_linedraw_to_cell(this, xleft, y, CLD_RIGHT);
+ for(x = xleft + 1; x < xright; ++x) {
+ canv_linedraw_to_cell(this, x, y, CLD_LEFT | CLD_RIGHT);
+ }
+ canv_linedraw_to_cell(this, xright, y, CLD_LEFT);
+ /* If xleft == xright, we have the cell drawn with CLD_LEFT | CLD_RIGHT.
+ * This is intentional.
+ */
+}
+
+void canv_put_vline(canv_t * this, sint32 x, sint32 ybottom, sint32 ytop)
+{
+ sint32 y;
+
+ CANV_CHECK_X(this, x);
+ CANV_CHECK_Y(this, ybottom);
+ CANV_CHECK_Y(this, ytop);
+ assert(ybottom >= ytop);
+
+ canv_linedraw_to_cell(this, x, ytop, CLD_DOWN);
+ for(y = ytop + 1; y < ybottom; ++y) {
+ canv_linedraw_to_cell(this, x, y, CLD_UP | CLD_DOWN);
+ }
+ canv_linedraw_to_cell(this, x, ybottom, CLD_UP);
+ /* If ytop == ybottom, we have the cell drawn with CLD_UP | CLD_DOWN.
+ * This is intentional.
+ */
+}
+
+void canv_put_black_box(
+ canv_t * this,
+ sint32 xleft,
+ sint32 ybottom,
+ sint32 xright,
+ sint32 ytop
+)
+{
+ sint32 x, y;
+
+ CANV_CHECK_X(this, xleft);
+ CANV_CHECK_Y(this, ybottom);
+ CANV_CHECK_X(this, xright);
+ CANV_CHECK_Y(this, ytop);
+ assert(xleft <= xright);
+ assert(ybottom >= ytop);
+
+ for(y = ytop; y <= ybottom; ++y) {
+ for(x = xleft; x <= xright; ++x) {
+ canv_allocate_cell(this, x, y, CCT_BLACK);
+ /* If the cell is already black this call fails, but the
+ * cell is then black anyway.
+ */
+ }
+ }
+}
+
+
+static const glyph_t linedraw_glyphs[16] = {
+ GLYPH_UNI_space,
+ GLYPH_UNI_bdrawlightrt,
+ GLYPH_UNI_bdrawlightup,
+ GLYPH_UNI_bdrawlightuprt,
+ GLYPH_UNI_bdrawlightlf,
+ GLYPH_UNI_bdrawlighthorz,
+ GLYPH_UNI_bdrawlightuplf,
+ GLYPH_UNI_bdrawlightuphorz,
+ GLYPH_UNI_bdrawlightdn,
+ GLYPH_UNI_bdrawlightdnrt,
+ GLYPH_UNI_bdrawlightvert,
+ GLYPH_UNI_bdrawlightvertrt,
+ GLYPH_UNI_bdrawlightdnlf,
+ GLYPH_UNI_bdrawlightdnhorz,
+ GLYPH_UNI_bdrawlightvertlf,
+ GLYPH_UNI_bdrawlightverthorz
+};
+
+void canv_write(const canv_t * this, FILE * f)
+{
+ linebuf_t lb;
+ canv_line_t * pl;
+ canv_cell_t * pc;
+ sint32 x, y;
+
+ linebuf_init(&lb, this->ncols);
+ /* lb's initial size should be about right for most lines */
+ for(y = 0; y < this->nlines; ++y) {
+ pl = this->lines + y;
+ assert(pl->cols_used <= this->ncols); /* consistency check */
+
+ linebuf_clear(&lb);
+ pc = pl->cells;
+ for(x = 0; x < pl->cols_used; ++x) {
+ switch(pc[x].type) {
+ case CCT_UNUSED:
+ linebuf_putg(&lb, GLYPH_UNI_space);
+ /*linebuf_putg(&lb, GLYPH_asterisk);*/
+ break;
+ case CCT_BLACK:
+ linebuf_putg(&lb, GLYPH_block);
+ break;
+ case CCT_LINEDRAW:
+ linebuf_putg(&lb, linedraw_glyphs[pc[x].data.draw]);
+ break;
+ case CCT_LINEBUF_CONT:
+ break;
+ case CCT_GLYPH:
+ linebuf_putg(&lb, pc[x].data.glyph);
+ break;
+ case CCT_LINEBUF_START:
+ linebuf_append(&lb, pc[x].data.lb);
+ break;
+ default:
+ NOTREACHED;
+ }
+ } /* for x */
+ outenc_write(f, &lb);
+ putc('\n', f);
+ } /* for y */
+
+ linebuf_done(&lb);
+}
+
+static canv_cell_t * canv_allocate_cell(
+ canv_t * this,
+ sint32 x,
+ sint32 y,
+ canv_celltype_t whatfor
+)
+{
+ canv_cell_t * res;
+ canv_line_t * l;
+
+ res = canv_cell(this, x, y);
+ if(res->type >= whatfor) {
+ pmesg(
+ 80,
+ "cell at (%ld, %ld) already used for %d, cannot use for %d\n",
+ x,
+ y,
+ res->type,
+ whatfor
+ );
+ return NULL;
+ }
+ res->type = whatfor;
+
+ l = this->lines + y;
+ if(l->cols_used < x + 1) l->cols_used = x + 1;
+ return res;
+}
+
+static canv_cell_t * canv_linedraw_to_cell(
+ canv_t * this,
+ sint32 x,
+ sint32 y,
+ canv_linedraw_t draw
+)
+{
+ canv_cell_t * res;
+
+ res = canv_cell(this, x, y);
+ if(res->type == CCT_LINEDRAW) {
+ /* already in use for line drawing */
+ res->data.draw |= draw;
+ }
+ else {
+ /* try to claim for line drawing */
+ res = canv_allocate_cell(this, x, y, CCT_LINEDRAW);
+ if(res != NULL) res->data.draw = draw;
+ }
+ return res;
+}