├── Makefile ├── README ├── clr.c ├── dev.c ├── dict.c ├── font.c ├── iset.c ├── pdf.c ├── pdfext.c ├── post.c ├── post.h ├── ps.c ├── sbuf.c └── txt.c /Makefile: -------------------------------------------------------------------------------- 1 | # neatpost's default font directory 2 | FDIR = /neatroff/font 3 | 4 | CC = cc 5 | CFLAGS = -Wall -O2 "-DTROFFFDIR=\"$(FDIR)\"" 6 | LDFLAGS = 7 | OBJS = post.o ps.o font.o dev.o clr.o dict.o iset.o sbuf.o 8 | OBJSPDF = post.o pdf.o pdfext.o font.o dev.o clr.o dict.o iset.o sbuf.o 9 | OBJSTXT = post.o txt.o font.o dev.o clr.o dict.o iset.o sbuf.o 10 | 11 | all: post pdf txt 12 | %.o: %.c post.h 13 | $(CC) -c $(CFLAGS) $< 14 | post: $(OBJS) 15 | $(CC) -o $@ $(OBJS) $(LDFLAGS) 16 | pdf: $(OBJSPDF) 17 | $(CC) -o $@ $(OBJSPDF) $(LDFLAGS) 18 | txt: $(OBJSTXT) 19 | $(CC) -o $@ $(OBJSTXT) $(LDFLAGS) 20 | clean: 21 | rm -f *.o post pdf txt 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | NEATPOST: NEATROFF'S POSTSCRIPT POSTPROCESSOR 2 | ============================================= 3 | 4 | Neatpost is Neatroff's postscript postprocessor. 5 | -------------------------------------------------------------------------------- /clr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "post.h" 6 | 7 | /* returns a static buffer */ 8 | char *clr_str(int c) 9 | { 10 | static char clr_buf[32]; 11 | if (!c) 12 | return "0"; 13 | sprintf(clr_buf, "#%02x%02x%02x", CLR_R(c), CLR_G(c), CLR_B(c)); 14 | return clr_buf; 15 | } 16 | 17 | static struct color { 18 | char *name; 19 | int value; 20 | } colors[] = { 21 | {"black", CLR_RGB(0, 0, 0)}, 22 | {"red", CLR_RGB(0xff, 0, 0)}, 23 | {"green", CLR_RGB(0, 0xff, 0)}, 24 | {"yellow", CLR_RGB(0xff, 0xff, 0)}, 25 | {"blue", CLR_RGB(0, 0, 0xff)}, 26 | {"magenta", CLR_RGB(0xff, 0, 0xff)}, 27 | {"cyan", CLR_RGB(0, 0xff, 0xff)}, 28 | {"white", CLR_RGB(0xff, 0xff, 0xff)}, 29 | }; 30 | 31 | /* read color component */ 32 | static int clrcomp(char *s, int len) 33 | { 34 | static char *digs = "0123456789abcdef"; 35 | int n = 0; 36 | int i; 37 | for (i = 0; i < len; i++) 38 | if (strchr(digs, tolower(s[i]))) 39 | n = n * 16 + (strchr(digs, tolower(s[i])) - digs); 40 | return len == 1 ? n * 255 / 15 : n; 41 | } 42 | 43 | int clr_get(char *s) 44 | { 45 | int i; 46 | if (s[0] == '#' && strlen(s) == 7) 47 | return CLR_RGB(clrcomp(s + 1, 2), clrcomp(s + 3, 2), clrcomp(s + 5, 2)); 48 | if (s[0] == '#' && strlen(s) == 4) 49 | return CLR_RGB(clrcomp(s + 1, 1), clrcomp(s + 2, 1), clrcomp(s + 3, 1)); 50 | if (isdigit(s[0]) && atoi(s) >= 0 && atoi(s) < LEN(colors)) 51 | return colors[atoi(s)].value; 52 | for (i = 0; i < LEN(colors); i++) 53 | if (!strcmp(colors[i].name, s)) 54 | return colors[i].value; 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /dev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "post.h" 6 | 7 | static char dev_dir[PATHLEN]; /* device directory */ 8 | static char dev_dev[PATHLEN]; /* output device name */ 9 | int dev_res; /* device resolution */ 10 | int dev_uwid; /* device unitwidth */ 11 | int dev_hor; /* minimum horizontal movement */ 12 | int dev_ver; /* minimum vertical movement */ 13 | 14 | /* mounted fonts */ 15 | static char fn_name[NFONTS][FNLEN]; /* font names */ 16 | static struct font *fn_font[NFONTS]; /* font structs */ 17 | static int fn_n; /* number of device fonts */ 18 | 19 | static void skipline(FILE* filp) 20 | { 21 | int c; 22 | do { 23 | c = getc(filp); 24 | } while (c != '\n' && c != EOF); 25 | } 26 | 27 | struct font *dev_fontopen(char *name) 28 | { 29 | char path[PATHLEN]; 30 | if (strchr(name, '/')) 31 | strcpy(path, name); 32 | else 33 | sprintf(path, "%s/dev%s/%s", dev_dir, dev_dev, name); 34 | return font_open(path); 35 | } 36 | 37 | int dev_mnt(int pos, char *id, char *name) 38 | { 39 | struct font *fn; 40 | if (pos >= NFONTS) 41 | return -1; 42 | fn = dev_fontopen(name); 43 | if (!fn) 44 | return -1; 45 | if (fn_font[pos]) 46 | font_close(fn_font[pos]); 47 | if (fn_name[pos] != name) /* ignore if fn_name[pos] is passed */ 48 | snprintf(fn_name[pos], sizeof(fn_name[pos]), "%s", id); 49 | fn_font[pos] = fn; 50 | return pos; 51 | } 52 | 53 | int dev_open(char *dir, char *dev) 54 | { 55 | char path[PATHLEN]; 56 | char tok[128]; 57 | int i; 58 | FILE *desc; 59 | strcpy(dev_dir, dir); 60 | strcpy(dev_dev, dev); 61 | sprintf(path, "%s/dev%s/DESC", dir, dev); 62 | desc = fopen(path, "r"); 63 | if (!desc) 64 | return 1; 65 | while (fscanf(desc, "%127s", tok) == 1) { 66 | if (tok[0] == '#') { 67 | skipline(desc); 68 | continue; 69 | } 70 | if (!strcmp("fonts", tok)) { 71 | fscanf(desc, "%d", &fn_n); 72 | for (i = 0; i < fn_n; i++) 73 | fscanf(desc, "%s", fn_name[i + 1]); 74 | fn_n++; 75 | continue; 76 | } 77 | if (!strcmp("sizes", tok)) { 78 | while (fscanf(desc, "%127s", tok) == 1) 79 | if (!strcmp("0", tok)) 80 | break; 81 | continue; 82 | } 83 | if (!strcmp("res", tok)) { 84 | fscanf(desc, "%d", &dev_res); 85 | continue; 86 | } 87 | if (!strcmp("unitwidth", tok)) { 88 | fscanf(desc, "%d", &dev_uwid); 89 | continue; 90 | } 91 | if (!strcmp("hor", tok)) { 92 | fscanf(desc, "%d", &dev_hor); 93 | continue; 94 | } 95 | if (!strcmp("ver", tok)) { 96 | fscanf(desc, "%d", &dev_ver); 97 | continue; 98 | } 99 | if (!strcmp("charset", tok)) 100 | break; 101 | skipline(desc); 102 | } 103 | fclose(desc); 104 | return 0; 105 | } 106 | 107 | void dev_close(void) 108 | { 109 | int i; 110 | for (i = 0; i < NFONTS; i++) { 111 | if (fn_font[i]) 112 | font_close(fn_font[i]); 113 | fn_font[i] = NULL; 114 | } 115 | } 116 | 117 | struct glyph *dev_glyph(char *c, int fn) 118 | { 119 | if (!strncmp("GID=", c, 4)) 120 | return font_glyph(fn_font[fn], c + 4); 121 | return font_find(fn_font[fn], c); 122 | } 123 | 124 | /* return the font struct at pos */ 125 | struct font *dev_font(int pos) 126 | { 127 | return pos >= 0 && pos < NFONTS ? fn_font[pos] : NULL; 128 | } 129 | 130 | int dev_fontid(struct font *fn) 131 | { 132 | int i; 133 | for (i = 0; i < NFONTS; i++) 134 | if (fn_font[i] == fn) 135 | return i; 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /dict.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "post.h" 5 | 6 | #define CNTMIN (1 << 10) 7 | 8 | struct dict { 9 | struct iset *map; 10 | char **key; 11 | int *val; 12 | int size; 13 | int n; 14 | int notfound; /* the value returned for missing keys */ 15 | int hashlen; /* the number of characters used for hashing */ 16 | int dupkeys; /* duplicate keys if set */ 17 | }; 18 | 19 | static void dict_extend(struct dict *d, int size) 20 | { 21 | d->key = mextend(d->key, d->size, size, sizeof(d->key[0])); 22 | d->val = mextend(d->val, d->size, size, sizeof(d->val[0])); 23 | d->size = size; 24 | } 25 | 26 | /* 27 | * initialise a dictionary 28 | * 29 | * notfound: the value returned for missing keys. 30 | * dupkeys: if nonzero, store a copy of keys inserted via dict_put(). 31 | * hashlen: the number of characters used for hashing 32 | */ 33 | struct dict *dict_make(int notfound, int dupkeys, int hashlen) 34 | { 35 | struct dict *d = malloc(sizeof(*d)); 36 | memset(d, 0, sizeof(*d)); 37 | d->n = 1; 38 | d->hashlen = hashlen ? hashlen : 32; 39 | d->dupkeys = dupkeys; 40 | d->notfound = notfound; 41 | d->map = iset_make(); 42 | dict_extend(d, CNTMIN); 43 | return d; 44 | } 45 | 46 | void dict_free(struct dict *d) 47 | { 48 | int i; 49 | if (d->dupkeys) 50 | for (i = 0; i < d->size; i++) 51 | free(d->key[i]); 52 | free(d->val); 53 | free(d->key); 54 | iset_free(d->map); 55 | free(d); 56 | } 57 | 58 | static int dict_hash(struct dict *d, char *key) 59 | { 60 | unsigned long hash = (unsigned char) *key++; 61 | int i = d->hashlen; 62 | while (--i > 0 && *key) 63 | hash = (hash << 5) + hash + (unsigned char) *key++; 64 | return hash & 0x3ff; 65 | } 66 | 67 | void dict_put(struct dict *d, char *key, int val) 68 | { 69 | int idx; 70 | if (d->n >= d->size) 71 | dict_extend(d, d->n + CNTMIN); 72 | if (d->dupkeys) { 73 | int len = strlen(key) + 1; 74 | char *dup = malloc(len); 75 | memcpy(dup, key, len); 76 | key = dup; 77 | } 78 | idx = d->n++; 79 | d->key[idx] = key; 80 | d->val[idx] = val; 81 | iset_put(d->map, dict_hash(d, key), idx); 82 | } 83 | 84 | /* return the index of key in d */ 85 | int dict_idx(struct dict *d, char *key) 86 | { 87 | int h = dict_hash(d, key); 88 | int *b = iset_get(d->map, h); 89 | int *r = b + iset_len(d->map, h); 90 | while (b && --r >= b) 91 | if (!strcmp(d->key[*r], key)) 92 | return *r; 93 | return -1; 94 | } 95 | 96 | char *dict_key(struct dict *d, int idx) 97 | { 98 | return d->key[idx]; 99 | } 100 | 101 | int dict_val(struct dict *d, int idx) 102 | { 103 | return d->val[idx]; 104 | } 105 | 106 | int dict_get(struct dict *d, char *key) 107 | { 108 | int idx = dict_idx(d, key); 109 | return idx >= 0 ? d->val[idx] : d->notfound; 110 | } 111 | 112 | /* match a prefix of key; in the first call, *idx should be -1 */ 113 | int dict_prefix(struct dict *d, char *key, int *pos) 114 | { 115 | int *r = iset_get(d->map, dict_hash(d, key)); 116 | while (r && r[++*pos] >= 0) { 117 | int idx = r[*pos]; 118 | int plen = strlen(d->key[idx]); 119 | if (!strncmp(d->key[idx], key, plen)) 120 | return d->val[idx]; 121 | } 122 | return d->notfound; 123 | } 124 | -------------------------------------------------------------------------------- /font.c: -------------------------------------------------------------------------------- 1 | /* font handling */ 2 | #include 3 | #include 4 | #include 5 | #include "post.h" 6 | 7 | struct font { 8 | char name[FNLEN]; 9 | char desc[1024]; 10 | char fontname[FNLEN]; 11 | char fontpath[1024]; 12 | int spacewid; 13 | struct glyph *gl; /* glyphs present in the font */ 14 | int gl_n, gl_sz; /* number of glyphs in the font */ 15 | struct dict *gl_dict; /* mapping from gl[i].id to i */ 16 | struct dict *ch_dict; /* charset mapping */ 17 | struct dict *ch_map; /* character aliases */ 18 | }; 19 | 20 | /* find a glyph by its name */ 21 | struct glyph *font_find(struct font *fn, char *name) 22 | { 23 | int i = dict_get(fn->ch_dict, name); 24 | if (i < 0) /* maybe a character alias */ 25 | i = dict_get(fn->ch_map, name); 26 | return i >= 0 ? fn->gl + i : NULL; 27 | } 28 | 29 | /* find a glyph by its device-dependent identifier */ 30 | struct glyph *font_glyph(struct font *fn, char *id) 31 | { 32 | int i = dict_get(fn->gl_dict, id); 33 | return i >= 0 ? &fn->gl[i] : NULL; 34 | } 35 | 36 | static int font_glyphput(struct font *fn, char *id, char *name, int type) 37 | { 38 | struct glyph *g; 39 | if (fn->gl_n == fn->gl_sz) { 40 | fn->gl_sz = fn->gl_sz + 1024; 41 | fn->gl = mextend(fn->gl, fn->gl_n, fn->gl_sz, sizeof(fn->gl[0])); 42 | } 43 | g = &fn->gl[fn->gl_n]; 44 | snprintf(g->id, sizeof(g->id), "%s", id); 45 | snprintf(g->name, sizeof(g->name), "%s", name); 46 | g->type = type; 47 | g->font = fn; 48 | dict_put(fn->gl_dict, g->id, fn->gl_n); 49 | return fn->gl_n++; 50 | } 51 | 52 | static void tilleol(FILE *fin, char *s) 53 | { 54 | int c = fgetc(fin); 55 | while (c != EOF && c != '\n') { 56 | *s++ = c; 57 | c = fgetc(fin); 58 | } 59 | *s = '\0'; 60 | if (c != EOF) 61 | ungetc(c, fin); 62 | } 63 | 64 | static int font_readchar(struct font *fn, FILE *fin, int *n, int *gid) 65 | { 66 | struct glyph *g; 67 | char tok[128]; 68 | char name[GNLEN]; 69 | char id[GNLEN]; 70 | int type; 71 | if (fscanf(fin, GNFMT " %127s", name, tok) != 2) 72 | return 1; 73 | if (!strcmp("---", name)) 74 | sprintf(name, "c%04d", *n); 75 | if (strcmp("\"", tok)) { 76 | if (fscanf(fin, "%d " GNFMT, &type, id) != 2) 77 | return 1; 78 | *gid = font_glyphput(fn, id, name, type); 79 | g = &fn->gl[*gid]; 80 | sscanf(tok, "%d", &g->wid); 81 | tilleol(fin, tok); 82 | if (sscanf(tok, "%d", &g->pos) != 1) 83 | g->pos = 0; 84 | dict_put(fn->ch_dict, name, *gid); 85 | (*n)++; 86 | } else { 87 | dict_put(fn->ch_map, name, *gid); 88 | } 89 | return 0; 90 | } 91 | 92 | static void skipline(FILE* filp) 93 | { 94 | int c; 95 | do { 96 | c = getc(filp); 97 | } while (c != '\n' && c != EOF); 98 | } 99 | 100 | struct font *font_open(char *path) 101 | { 102 | struct font *fn; 103 | int ch_g = -1; /* last glyph in the charset */ 104 | int ch_n = 0; /* number of glyphs in the charset */ 105 | char tok[128]; 106 | FILE *fin; 107 | fin = fopen(path, "r"); 108 | if (!fin) 109 | return NULL; 110 | fn = malloc(sizeof(*fn)); 111 | if (!fn) { 112 | fclose(fin); 113 | return NULL; 114 | } 115 | memset(fn, 0, sizeof(*fn)); 116 | snprintf(fn->desc, sizeof(fn->desc), "%s", path); 117 | fn->gl_dict = dict_make(-1, 1, 0); 118 | fn->ch_dict = dict_make(-1, 1, 0); 119 | fn->ch_map = dict_make(-1, 1, 0); 120 | while (fscanf(fin, "%127s", tok) == 1) { 121 | if (!strcmp("char", tok)) { 122 | font_readchar(fn, fin, &ch_n, &ch_g); 123 | } else if (!strcmp("spacewidth", tok)) { 124 | fscanf(fin, "%d", &fn->spacewid); 125 | } else if (!strcmp("name", tok)) { 126 | fscanf(fin, "%s", fn->name); 127 | } else if (!strcmp("fontname", tok)) { 128 | fscanf(fin, "%s", fn->fontname); 129 | } else if (!strcmp("fontpath", tok)) { 130 | int c = fgetc(fin); 131 | while (c == ' ') 132 | c = fgetc(fin); 133 | ungetc(c, fin); 134 | tilleol(fin, fn->fontpath); 135 | } else if (!strcmp("ligatures", tok)) { 136 | while (fscanf(fin, "%s", tok) == 1) 137 | if (!strcmp("0", tok)) 138 | break; 139 | } else if (!strcmp("charset", tok)) { 140 | while (!font_readchar(fn, fin, &ch_n, &ch_g)) 141 | ; 142 | break; 143 | } 144 | skipline(fin); 145 | } 146 | fclose(fin); 147 | return fn; 148 | } 149 | 150 | void font_close(struct font *fn) 151 | { 152 | dict_free(fn->gl_dict); 153 | dict_free(fn->ch_dict); 154 | dict_free(fn->ch_map); 155 | free(fn->gl); 156 | free(fn); 157 | } 158 | 159 | /* return width w for the given font and size */ 160 | int font_wid(struct font *fn, int sz, int w) 161 | { 162 | return (w * sz + dev_uwid / 2) / dev_uwid; 163 | } 164 | 165 | /* space width for the give word space or sentence space */ 166 | int font_swid(struct font *fn, int sz) 167 | { 168 | return font_wid(fn, sz, fn->spacewid); 169 | } 170 | 171 | char *font_name(struct font *fn) 172 | { 173 | return fn->fontname[0] ? fn->fontname : fn->name; 174 | } 175 | 176 | char *font_path(struct font *fn) 177 | { 178 | return fn->fontpath; 179 | } 180 | 181 | int font_glnum(struct font *fn, struct glyph *g) 182 | { 183 | return g - fn->gl; 184 | } 185 | 186 | struct glyph *font_glget(struct font *fn, int id) 187 | { 188 | return id >= 0 && id < fn->gl_n ? &fn->gl[id] : NULL; 189 | } 190 | 191 | char *font_desc(struct font *fn) 192 | { 193 | return fn->desc; 194 | } 195 | -------------------------------------------------------------------------------- /iset.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "post.h" 4 | 5 | #define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1)) 6 | #define CNTMIN (1 << 10) 7 | #define CNTMAX (1 << 20) 8 | 9 | /* iset structure to map integers to sets */ 10 | struct iset { 11 | int **set; 12 | int *sz; 13 | int *len; 14 | int cnt; 15 | }; 16 | 17 | static void iset_extend(struct iset *iset, int cnt) 18 | { 19 | iset->set = mextend(iset->set, iset->cnt, cnt, sizeof(iset->set[0])); 20 | iset->sz = mextend(iset->sz, iset->cnt, cnt, sizeof(iset->sz[0])); 21 | iset->len = mextend(iset->len, iset->cnt, cnt, sizeof(iset->len[0])); 22 | iset->cnt = cnt; 23 | } 24 | 25 | struct iset *iset_make(void) 26 | { 27 | struct iset *iset = malloc(sizeof(*iset)); 28 | memset(iset, 0, sizeof(*iset)); 29 | iset_extend(iset, CNTMIN); 30 | return iset; 31 | } 32 | 33 | void iset_free(struct iset *iset) 34 | { 35 | int i; 36 | for (i = 0; i < iset->cnt; i++) 37 | free(iset->set[i]); 38 | free(iset->set); 39 | free(iset->len); 40 | free(iset->sz); 41 | free(iset); 42 | } 43 | 44 | int *iset_get(struct iset *iset, int key) 45 | { 46 | return key >= 0 && key < iset->cnt ? iset->set[key] : NULL; 47 | } 48 | 49 | int iset_len(struct iset *iset, int key) 50 | { 51 | return key >= 0 && key < iset->cnt ? iset->len[key] : 0; 52 | } 53 | 54 | void iset_put(struct iset *iset, int key, int ent) 55 | { 56 | if (key < 0 || key >= CNTMAX) 57 | return; 58 | if (key >= iset->cnt) 59 | iset_extend(iset, ALIGN(key + 1, CNTMIN)); 60 | if (key >= 0 && key < iset->cnt && iset->len[key] + 1 >= iset->sz[key]) { 61 | int olen = iset->sz[key]; 62 | int nlen = iset->sz[key] * 2 + 8; 63 | void *nset = malloc(nlen * sizeof(iset->set[key][0])); 64 | if (iset->set[key]) { 65 | memcpy(nset, iset->set[key], 66 | olen * sizeof(iset->set[key][0])); 67 | free(iset->set[key]); 68 | } 69 | iset->sz[key] = nlen; 70 | iset->set[key] = nset; 71 | } 72 | iset->set[key][iset->len[key]++] = ent; 73 | iset->set[key][iset->len[key]] = -1; 74 | } 75 | -------------------------------------------------------------------------------- /pdf.c: -------------------------------------------------------------------------------- 1 | /* PDF post-processor functions */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "post.h" 10 | 11 | static char pdf_title[256]; /* document title */ 12 | static char pdf_author[256]; /* document author */ 13 | static int pdf_width; /* page width */ 14 | static int pdf_height; /* page height */ 15 | static int pdf_linewid; /* line width in thousands of ems */ 16 | static int pdf_linecap = 1; /* line cap style: 0 (butt), 1 (round), 2 (projecting square) */ 17 | static int pdf_linejoin = 1; /* line join style: 0 (miter), 1 (round), 2 (bevel) */ 18 | static int pdf_pages; /* pages object id */ 19 | static int pdf_root; /* root object id */ 20 | static int pdf_pos; /* current pdf file offset */ 21 | static int *obj_off; /* object offsets */ 22 | static int obj_sz, obj_n; /* number of pdf objects */ 23 | static int *page_id; /* page object ids */ 24 | static int page_sz, page_n; /* number of pages */ 25 | static int pdf_outline; /* pdf outline hierarchiy */ 26 | static int pdf_dests; /* named destinations */ 27 | 28 | static struct sbuf *pg; /* current page contents */ 29 | static int o_f, o_s, o_m; /* font and size */ 30 | static int o_h, o_v; /* current user position */ 31 | static int p_h, p_v; /* current output position */ 32 | static int o_i, p_i; /* output and pdf fonts (indices into pfont[]) */ 33 | static int p_f, p_s, p_m; /* output font */ 34 | static int o_queued; /* queued character type */ 35 | static char o_iset[1024]; /* fonts accesssed in this page */ 36 | static int xobj[128]; /* page xobject object ids */ 37 | static int xobj_n; /* number of xobjects in this page */ 38 | static int ann[128]; /* page annotations */ 39 | static int ann_n; /* number of annotations in this page */ 40 | 41 | /* loaded PDF fonts */ 42 | struct pfont { 43 | char name[128]; /* font PostScript name */ 44 | char path[1024]; /* font path */ 45 | char desc[1024]; /* font descriptor path */ 46 | int gbeg; /* the first glyph */ 47 | int gend; /* the last glyph */ 48 | int sub; /* subfont number */ 49 | int obj; /* the font object */ 50 | int des; /* font descriptor */ 51 | int cid; /* CID-indexed */ 52 | }; 53 | 54 | static struct pfont *pfonts; 55 | static int pfonts_n, pfonts_sz; 56 | 57 | /* print formatted pdf output */ 58 | static void pdfout(char *s, ...) 59 | { 60 | va_list ap; 61 | va_start(ap, s); 62 | pdf_pos += vprintf(s, ap); 63 | va_end(ap); 64 | } 65 | 66 | /* print pdf output */ 67 | static void pdfmem(char *s, int len) 68 | { 69 | fwrite(s, len, 1, stdout); 70 | pdf_pos += len; 71 | } 72 | 73 | /* allocate an object number */ 74 | static int obj_map(void) 75 | { 76 | if (obj_n == obj_sz) { 77 | obj_sz += 1024; 78 | obj_off = mextend(obj_off, obj_n, obj_sz, sizeof(obj_off[0])); 79 | } 80 | return obj_n++; 81 | } 82 | 83 | /* start the definition of an object */ 84 | static int obj_beg(int id) 85 | { 86 | if (id <= 0) 87 | id = obj_map(); 88 | obj_off[id] = pdf_pos; 89 | pdfout("%d 0 obj\n", id); 90 | return id; 91 | } 92 | 93 | /* end an object definition */ 94 | static void obj_end(void) 95 | { 96 | pdfout("endobj\n\n"); 97 | } 98 | 99 | void out(char *s, ...) 100 | { 101 | } 102 | 103 | /* the length of the clear-text, encrypted, and fixed-content portions */ 104 | static int type1lengths(char *t1, int l, int *l1, int *l2, int *l3) 105 | { 106 | int i; 107 | char *cleartext = t1; 108 | char *encrypted = NULL; 109 | char *fixedcont = NULL; 110 | for (i = 0; i < l - 5 && !encrypted; i++) 111 | if (t1[i] == 'e' && !memcmp("eexec", t1 + i, 5)) 112 | encrypted = t1 + i; 113 | if (!encrypted) 114 | return 1; 115 | for (; i < l - 512 && !fixedcont; i++) 116 | if (t1[i] == '0' && !memcmp("00000", t1 + i, 5)) 117 | fixedcont = t1 + i; 118 | *l1 = encrypted - cleartext; 119 | *l2 = fixedcont ? fixedcont - cleartext : 0; 120 | *l3 = fixedcont ? t1 + l - fixedcont : 0; 121 | return 0; 122 | } 123 | 124 | /* return font type: 't': TrueType, '1': Type 1, 'o': OpenType */ 125 | static int fonttype(char *path) 126 | { 127 | char *ext = strrchr(path, '.'); 128 | if (ext && !strcmp(".ttf", ext)) 129 | return 't'; 130 | if (ext && !strcmp(".otf", ext)) 131 | return 't'; 132 | if (ext && (!strcmp(".ttc", ext) || !strcmp(".otc", ext))) 133 | return 't'; 134 | return '1'; 135 | } 136 | 137 | /* write the object corresponding to the given font */ 138 | static void pfont_write(struct pfont *ps) 139 | { 140 | int i; 141 | int enc_obj; 142 | struct font *fn = dev_fontopen(ps->desc); 143 | /* the encoding object */ 144 | enc_obj = obj_beg(0); 145 | pdfout("<<\n"); 146 | pdfout(" /Type /Encoding\n"); 147 | pdfout(" /Differences [ %d", ps->gbeg % 256); 148 | for (i = ps->gbeg; i <= ps->gend; i++) 149 | pdfout(" /%s", font_glget(fn, i)->id); 150 | pdfout(" ]\n"); 151 | pdfout(">>\n"); 152 | obj_end(); 153 | /* the font object */ 154 | obj_beg(ps->obj); 155 | pdfout("<<\n"); 156 | pdfout(" /Type /Font\n"); 157 | if (fonttype(ps->path) == 't') 158 | pdfout(" /Subtype /TrueType\n"); 159 | else 160 | pdfout(" /Subtype /Type1\n"); 161 | pdfout(" /BaseFont /%s\n", ps->name); 162 | pdfout(" /FirstChar %d\n", ps->gbeg % 256); 163 | pdfout(" /LastChar %d\n", ps->gend % 256); 164 | pdfout(" /Widths ["); 165 | for (i = ps->gbeg; i <= ps->gend; i++) 166 | pdfout(" %d", (long long) font_glget(fn, i)->wid * 100 * 72 / dev_res); 167 | pdfout(" ]\n"); 168 | pdfout(" /FontDescriptor %d 0 R\n", ps->des); 169 | pdfout(" /Encoding %d 0 R\n", enc_obj); 170 | pdfout(">>\n"); 171 | obj_end(); 172 | font_close(fn); 173 | } 174 | 175 | static void encodehex(struct sbuf *d, char *s, int n) 176 | { 177 | static char hex[] = "0123456789ABCDEF"; 178 | int i; 179 | for (i = 0; i < n; i++) { 180 | sbuf_chr(d, hex[((unsigned char) s[i]) >> 4]); 181 | sbuf_chr(d, hex[((unsigned char) s[i]) & 0x0f]); 182 | if (i % 40 == 39 && i + 1 < n) 183 | sbuf_chr(d, '\n'); 184 | } 185 | sbuf_str(d, ">\n"); 186 | } 187 | 188 | /* write the object corresponding to this CID font */ 189 | static void pfont_writecid(struct pfont *ps) 190 | { 191 | int cid_obj; 192 | struct font *fn = dev_fontopen(ps->desc); 193 | int gcnt = 0; 194 | int i; 195 | /* CIDFont */ 196 | cid_obj = obj_beg(0); 197 | pdfout("<<\n"); 198 | pdfout(" /Type /Font\n"); 199 | pdfout(" /Subtype /CIDFontType2\n"); 200 | pdfout(" /BaseFont /%s\n", ps->name); 201 | pdfout(" /CIDSystemInfo <>\n"); 202 | pdfout(" /FontDescriptor %d 0 R\n", ps->des); 203 | pdfout(" /DW 1000\n"); 204 | while (font_glget(fn, gcnt)) 205 | gcnt++; 206 | pdfout(" /W [ %d [", ps->gbeg); 207 | for (i = ps->gbeg; i <= ps->gend; i++) 208 | pdfout(" %d", (long long) font_glget(fn, i)->wid * 100 * 72 / dev_res); 209 | pdfout(" ] ]\n"); 210 | pdfout(">>\n"); 211 | obj_end(); 212 | /* the font object */ 213 | obj_beg(ps->obj); 214 | pdfout("<<\n"); 215 | pdfout(" /Type /Font\n"); 216 | pdfout(" /Subtype /Type0\n"); 217 | pdfout(" /BaseFont /%s\n", ps->name); 218 | pdfout(" /Encoding /Identity-H\n"); 219 | pdfout(" /DescendantFonts [%d 0 R]\n", cid_obj); 220 | pdfout(">>\n"); 221 | obj_end(); 222 | font_close(fn); 223 | } 224 | 225 | /* write font descriptor; returns its object ID */ 226 | static int writedesc(struct font *fn) 227 | { 228 | int str_obj = -1; 229 | int des_obj; 230 | char buf[1 << 10]; 231 | int fntype = fonttype(font_path(fn)); 232 | if (fntype == '1' || fntype == 't') { 233 | int fd = open(font_path(fn), O_RDONLY); 234 | struct sbuf *ffsb = sbuf_make(); 235 | struct sbuf *sb = sbuf_make(); 236 | int l1 = 0, l2 = 0, l3 = 0; 237 | int nr; 238 | /* reading the font file */ 239 | while ((nr = read(fd, buf, sizeof(buf))) > 0) 240 | sbuf_mem(ffsb, buf, nr); 241 | close(fd); 242 | l1 = sbuf_len(ffsb); 243 | /* initialize Type 1 lengths */ 244 | if (fntype == '1') { 245 | if (type1lengths(sbuf_buf(ffsb), sbuf_len(ffsb), 246 | &l1, &l2, &l3)) 247 | l1 = 0; 248 | /* remove the fixed-content portion of the font */ 249 | if (l3) 250 | sbuf_cut(ffsb, l1 + l2); 251 | l1 -= l3; 252 | l3 = 0; 253 | } 254 | /* encoding file contents */ 255 | encodehex(sb, sbuf_buf(ffsb), sbuf_len(ffsb)); 256 | /* write font data if it has nonzero length */ 257 | if (l1) { 258 | str_obj = obj_beg(0); 259 | pdfout("<<\n"); 260 | pdfout(" /Filter /ASCIIHexDecode\n"); 261 | pdfout(" /Length %d\n", sbuf_len(sb)); 262 | pdfout(" /Length1 %d\n", l1); 263 | if (fntype == '1') 264 | pdfout(" /Length2 %d\n", l2); 265 | if (fntype == '1') 266 | pdfout(" /Length3 %d\n", l3); 267 | pdfout(">>\n"); 268 | pdfout("stream\n"); 269 | pdfmem(sbuf_buf(sb), sbuf_len(sb)); 270 | pdfout("endstream\n"); 271 | obj_end(); 272 | } 273 | sbuf_free(ffsb); 274 | sbuf_free(sb); 275 | } 276 | /* the font descriptor */ 277 | des_obj = obj_beg(0); 278 | pdfout("<<\n"); 279 | pdfout(" /Type /FontDescriptor\n"); 280 | pdfout(" /FontName /%s\n", font_name(fn)); 281 | pdfout(" /Flags 32\n"); 282 | pdfout(" /FontBBox [-1000 -1000 1000 1000]\n"); 283 | pdfout(" /MissingWidth 1000\n"); 284 | pdfout(" /StemV 100\n"); 285 | pdfout(" /ItalicAngle 0\n"); 286 | pdfout(" /CapHeight 100\n"); 287 | pdfout(" /Ascent 100\n"); 288 | pdfout(" /Descent 100\n"); 289 | if (str_obj >= 0) 290 | pdfout(" /FontFile%s %d 0 R\n", 291 | fntype == 't' ? "2" : "", str_obj); 292 | pdfout(">>\n"); 293 | obj_end(); 294 | return des_obj; 295 | } 296 | 297 | static int pfont_find(struct glyph *g) 298 | { 299 | struct font *fn = g->font; 300 | char *name = font_name(fn); 301 | struct pfont *ps = NULL; 302 | int fntype = fonttype(font_path(fn)); 303 | int sub = fntype == '1' ? font_glnum(fn, g) / 256 : 0; 304 | int i; 305 | for (i = 0; i < pfonts_n; i++) 306 | if (!strcmp(name, pfonts[i].name) && pfonts[i].sub == sub) 307 | return i; 308 | if (pfonts_n == pfonts_sz) { 309 | pfonts_sz += 16; 310 | pfonts = mextend(pfonts, pfonts_n, 311 | pfonts_sz, sizeof(pfonts[0])); 312 | } 313 | ps = &pfonts[pfonts_n]; 314 | snprintf(ps->name, sizeof(ps->name), "%s", name); 315 | snprintf(ps->path, sizeof(ps->path), "%s", font_path(fn)); 316 | snprintf(ps->desc, sizeof(ps->desc), "%s", font_desc(fn)); 317 | ps->cid = fntype == 't'; 318 | ps->obj = obj_map(); 319 | ps->sub = sub; 320 | ps->gbeg = 1 << 20; 321 | for (i = 0; i < pfonts_n; i++) 322 | if (!strcmp(pfonts[i].name, ps->name)) 323 | break; 324 | if (i < pfonts_n) 325 | ps->des = pfonts[i].des; 326 | else 327 | ps->des = writedesc(fn); 328 | return pfonts_n++; 329 | } 330 | 331 | static void pfont_done(void) 332 | { 333 | int i; 334 | for (i = 0; i < pfonts_n; i++) { 335 | if (pfonts[i].cid) 336 | pfont_writecid(&pfonts[i]); 337 | else 338 | pfont_write(&pfonts[i]); 339 | } 340 | free(pfonts); 341 | } 342 | 343 | static void o_flush(void) 344 | { 345 | if (o_queued == 1) 346 | sbuf_printf(pg, ">] TJ\n"); 347 | o_queued = 0; 348 | } 349 | 350 | static int o_loadfont(struct glyph *g) 351 | { 352 | int fn = pfont_find(g); 353 | o_iset[fn] = 1; 354 | return fn; 355 | } 356 | 357 | /* like pdfpos() but assume that uh and uv are multiplied by 100 */ 358 | static char *pdfpos00(int uh, int uv) 359 | { 360 | static char buf[64]; 361 | int h = (long long) uh * 72 / dev_res; 362 | int v = (long long) pdf_height * 100 - (long long) uv * 72 / dev_res; 363 | sprintf(buf, "%s%d.%02d %s%d.%02d", 364 | h < 0 ? "-" : "", abs(h) / 100, abs(h) % 100, 365 | v < 0 ? "-" : "", abs(v) / 100, abs(v) % 100); 366 | return buf; 367 | } 368 | 369 | /* convert troff position to pdf position; returns a static buffer */ 370 | static char *pdfpos(int uh, int uv) 371 | { 372 | return pdfpos00(uh * 100, uv * 100); 373 | } 374 | 375 | /* troff length to thousands of a unit of text space; returns a static buffer */ 376 | static char *pdfunit(int uh, int sz) 377 | { 378 | static char buf[64]; 379 | int h = (long long) uh * 1000 * 72 / sz / dev_res; 380 | sprintf(buf, "%s%d", h < 0 ? "-" : "", abs(h)); 381 | return buf; 382 | } 383 | 384 | /* convert troff color to pdf color; returns a static buffer */ 385 | static char *pdfcolor(int m) 386 | { 387 | static char buf[64]; 388 | int r = CLR_R(m) * 1000 / 255; 389 | int g = CLR_G(m) * 1000 / 255; 390 | int b = CLR_B(m) * 1000 / 255; 391 | sbuf_printf(pg, "%d.%03d %d.%03d %d.%03d", 392 | r / 1000, r % 1000, g / 1000, g % 1000, b / 1000, b % 1000); 393 | return buf; 394 | } 395 | 396 | static void o_queue(struct glyph *g) 397 | { 398 | int gid; 399 | if (o_v != p_v) { 400 | o_flush(); 401 | sbuf_printf(pg, "1 0 0 1 %s Tm\n", pdfpos(o_h, o_v)); 402 | p_h = o_h; 403 | p_v = o_v; 404 | } 405 | if (!o_queued) 406 | sbuf_printf(pg, "[<"); 407 | o_queued = 1; 408 | if (o_h != p_h) 409 | sbuf_printf(pg, "> %s <", pdfunit(p_h - o_h, o_s)); 410 | /* printing glyph identifier */ 411 | gid = font_glnum(g->font, g); 412 | if (pfonts[o_i].cid) 413 | sbuf_printf(pg, "%04x", gid); 414 | else 415 | sbuf_printf(pg, "%02x", gid % 256); 416 | /* updating gbeg and gend */ 417 | if (gid < pfonts[o_i].gbeg) 418 | pfonts[o_i].gbeg = gid; 419 | if (gid > pfonts[o_i].gend) 420 | pfonts[o_i].gend = gid; 421 | /* advancing */ 422 | p_h = o_h + font_wid(g->font, o_s, g->wid); 423 | } 424 | 425 | static void out_fontup(void) 426 | { 427 | if (o_m != p_m) { 428 | o_flush(); 429 | sbuf_printf(pg, "%s rg\n", pdfcolor(o_m)); 430 | p_m = o_m; 431 | } 432 | if (o_i >= 0 && (o_i != p_i || o_s != p_s)) { 433 | struct pfont *ps = &pfonts[o_i]; 434 | o_flush(); 435 | if (ps->cid) 436 | sbuf_printf(pg, "/%s %d Tf\n", ps->name, o_s); 437 | else 438 | sbuf_printf(pg, "/%s.%d %d Tf\n", ps->name, ps->sub, o_s); 439 | p_i = o_i; 440 | p_s = o_s; 441 | } 442 | } 443 | 444 | void outc(char *c) 445 | { 446 | struct glyph *g; 447 | struct font *fn; 448 | g = dev_glyph(c, o_f); 449 | fn = g ? g->font : dev_font(o_f); 450 | if (!g) { 451 | outrel(*c == ' ' && fn ? font_swid(fn, o_s) : 1, 0); 452 | return; 453 | } 454 | o_i = o_loadfont(g); 455 | out_fontup(); 456 | o_queue(g); 457 | } 458 | 459 | void outh(int h) 460 | { 461 | o_h = h; 462 | } 463 | 464 | void outv(int v) 465 | { 466 | o_v = v; 467 | } 468 | 469 | void outrel(int h, int v) 470 | { 471 | o_h += h; 472 | o_v += v; 473 | } 474 | 475 | void outfont(int f) 476 | { 477 | if (dev_font(f)) 478 | o_f = f; 479 | } 480 | 481 | void outsize(int s) 482 | { 483 | if (s > 0) 484 | o_s = s; 485 | } 486 | 487 | void outcolor(int c) 488 | { 489 | o_m = c; 490 | } 491 | 492 | void outrotate(int deg) 493 | { 494 | } 495 | 496 | void outeps(char *eps, int hwid, int vwid) 497 | { 498 | } 499 | 500 | /* return a copy of a PDF object; returns a static buffer */ 501 | static char *pdf_copy(char *pdf, int len, int pos) 502 | { 503 | static char buf[1 << 12]; 504 | int datlen; 505 | pos += pdf_ws(pdf, len, pos); 506 | datlen = pdf_len(pdf, len, pos); 507 | if (datlen > sizeof(buf) - 1) 508 | datlen = sizeof(buf) - 1; 509 | memcpy(buf, pdf + pos, datlen); 510 | buf[datlen] = '\0'; 511 | return buf; 512 | } 513 | 514 | static void pdf_dictcopy(char *pdf, int len, int pos, struct sbuf *sb); 515 | 516 | /* write stream to sb */ 517 | static int pdf_strcopy(char *pdf, int len, int pos, struct sbuf *sb) 518 | { 519 | int slen, val; 520 | int beg; 521 | if ((val = pdf_dval_val(pdf, len, pos, "/Length")) < 0) 522 | return -1; 523 | slen = atoi(pdf + val); 524 | pos = pos + pdf_len(pdf, len, pos); 525 | pos += pdf_ws(pdf, len, pos); 526 | if (pos + slen + 15 > len) 527 | return -1; 528 | beg = pos; 529 | pos += strlen("stream"); 530 | if (pdf[pos] == '\r') 531 | pos++; 532 | pos += 1 + slen; 533 | if (pdf[pos] == '\r' || pdf[pos] == ' ') 534 | pos++; 535 | if (pdf[pos] == '\n') 536 | pos++; 537 | pos += strlen("endstream") + 1; 538 | sbuf_mem(sb, pdf + beg, pos - beg); 539 | return 0; 540 | } 541 | 542 | /* copy a PDF object and return its new identifier */ 543 | static int pdf_objcopy(char *pdf, int len, int pos) 544 | { 545 | int id; 546 | if ((pos = pdf_ref(pdf, len, pos)) < 0) 547 | return -1; 548 | if (pdf_type(pdf, len, pos) == 'd') { 549 | struct sbuf *sb = sbuf_make(); 550 | pdf_dictcopy(pdf, len, pos, sb); 551 | sbuf_chr(sb, '\n'); 552 | if (pdf_dval(pdf, len, pos, "/Length") >= 0) 553 | pdf_strcopy(pdf, len, pos, sb); 554 | id = obj_beg(0); 555 | pdfmem(sbuf_buf(sb), sbuf_len(sb)); 556 | obj_end(); 557 | sbuf_free(sb); 558 | } else { 559 | id = obj_beg(0); 560 | pdfmem(pdf + pos, pdf_len(pdf, len, pos)); 561 | pdfout("\n"); 562 | obj_end(); 563 | } 564 | return id; 565 | } 566 | 567 | /* copy a PDF dictionary recursively */ 568 | static void pdf_dictcopy(char *pdf, int len, int pos, struct sbuf *sb) 569 | { 570 | int i; 571 | int key, val, id; 572 | sbuf_printf(sb, "<<"); 573 | for (i = 0; ; i++) { 574 | if ((key = pdf_dkey(pdf, len, pos, i)) < 0) 575 | break; 576 | sbuf_printf(sb, " %s", pdf_copy(pdf, len, key)); 577 | val = pdf_dval(pdf, len, pos, pdf_copy(pdf, len, key)); 578 | if (pdf_type(pdf, len, val) == 'r') { 579 | if ((id = pdf_objcopy(pdf, len, val)) >= 0) 580 | sbuf_printf(sb, " %d 0 R", id); 581 | } else { 582 | sbuf_printf(sb, " %s", pdf_copy(pdf, len, val)); 583 | } 584 | } 585 | sbuf_printf(sb, " >>"); 586 | } 587 | 588 | /* copy resources dictionary */ 589 | static void pdf_rescopy(char *pdf, int len, int pos, struct sbuf *sb) 590 | { 591 | char *res_fields[] = {"/ProcSet", "/ExtGState", "/ColorSpace", 592 | "/Pattern", "/Shading", "/Properties", "/Font", "/XObject"}; 593 | int res, i; 594 | sbuf_printf(sb, " /Resources <<\n"); 595 | for (i = 0; i < LEN(res_fields); i++) { 596 | if ((res = pdf_dval_val(pdf, len, pos, res_fields[i])) >= 0) { 597 | if (pdf_type(pdf, len, res) == 'd') { 598 | sbuf_printf(sb, " %s ", res_fields[i]); 599 | pdf_dictcopy(pdf, len, res, sb); 600 | sbuf_printf(sb, "\n"); 601 | } else { 602 | sbuf_printf(sb, " %s %s\n", res_fields[i], 603 | pdf_copy(pdf, len, res)); 604 | } 605 | } 606 | } 607 | sbuf_printf(sb, " >>\n"); 608 | } 609 | 610 | static int pdfbbox100(char *pdf, int len, int pos, int dim[4]) 611 | { 612 | int val; 613 | int i; 614 | for (i = 0; i < 4; i++) { 615 | int n = 0, f1 = 0, f2 = 0; 616 | if ((val = pdf_lval(pdf, len, pos, i)) < 0) 617 | return -1; 618 | for (; isdigit((unsigned char) pdf[val]); val++) 619 | n = n * 10 + pdf[val] - '0'; 620 | if (pdf[val] == '.') { 621 | if (isdigit((unsigned char) pdf[val + 1])) { 622 | f1 = pdf[val + 1] - '0'; 623 | if (isdigit((unsigned char) pdf[val + 2])) 624 | f2 = pdf[val + 2] - '0'; 625 | } 626 | } 627 | dim[i] = n * 100 + f1 * 10 + f2; 628 | } 629 | return 0; 630 | } 631 | 632 | static int pdfext(char *pdf, int len, int hwid, int vwid) 633 | { 634 | char *cont_fields[] = {"/Filter", "/DecodeParms"}; 635 | int trailer, root, cont, pages, page1, res; 636 | int kids_val, page1_val, val, bbox; 637 | int xobj_id, length; 638 | int dim[4]; 639 | int hzoom = 100, vzoom = 100; 640 | struct sbuf *sb; 641 | int i; 642 | if (xobj_n == LEN(xobj)) 643 | return -1; 644 | if ((trailer = pdf_trailer(pdf, len)) < 0) 645 | return -1; 646 | if ((root = pdf_dval_obj(pdf, len, trailer, "/Root")) < 0) 647 | return -1; 648 | if ((pages = pdf_dval_obj(pdf, len, root, "/Pages")) < 0) 649 | return -1; 650 | if ((kids_val = pdf_dval_val(pdf, len, pages, "/Kids")) < 0) 651 | return -1; 652 | if ((page1_val = pdf_lval(pdf, len, kids_val, 0)) < 0) 653 | return -1; 654 | if ((page1 = pdf_ref(pdf, len, page1_val)) < 0) 655 | return -1; 656 | if ((cont = pdf_dval_obj(pdf, len, page1, "/Contents")) < 0) 657 | return -1; 658 | if ((val = pdf_dval_val(pdf, len, cont, "/Length")) < 0) 659 | return -1; 660 | res = pdf_dval_val(pdf, len, page1, "/Resources"); 661 | length = atoi(pdf + val); 662 | bbox = pdf_dval_val(pdf, len, page1, "/MediaBox"); 663 | if (bbox < 0) 664 | bbox = pdf_dval_val(pdf, len, pages, "/MediaBox"); 665 | if (bbox >= 0 && !pdfbbox100(pdf, len, bbox, dim)) { 666 | if (hwid > 0) 667 | hzoom = (long long) hwid * (100 * 7200 / dev_res) / (dim[2] - dim[0]); 668 | if (vwid > 0) 669 | vzoom = (long long) vwid * (100 * 7200 / dev_res) / (dim[3] - dim[1]); 670 | if (vwid <= 0) 671 | vzoom = hzoom; 672 | if (hwid <= 0) 673 | hzoom = vzoom; 674 | } 675 | sb = sbuf_make(); 676 | sbuf_printf(sb, "<<\n"); 677 | sbuf_printf(sb, " /Type /XObject\n"); 678 | sbuf_printf(sb, " /Subtype /Form\n"); 679 | sbuf_printf(sb, " /FormType 1\n"); 680 | if (bbox >= 0) 681 | sbuf_printf(sb, " /BBox %s\n", pdf_copy(pdf, len, bbox)); 682 | sbuf_printf(sb, " /Matrix [%d.%02d 0 0 %d.%02d %s]\n", 683 | hzoom / 100, hzoom % 100, vzoom / 100, vzoom % 100, 684 | pdfpos(o_h, o_v)); 685 | if (res >= 0) 686 | pdf_rescopy(pdf, len, res, sb); 687 | sbuf_printf(sb, " /Length %d\n", length); 688 | for (i = 0; i < LEN(cont_fields); i++) 689 | if ((val = pdf_dval_val(pdf, len, cont, cont_fields[i])) >= 0) 690 | sbuf_printf(sb, " %s %s\n", cont_fields[i], 691 | pdf_copy(pdf, len, val)); 692 | sbuf_printf(sb, ">>\n"); 693 | pdf_strcopy(pdf, len, cont, sb); 694 | xobj_id = obj_beg(0); 695 | pdfmem(sbuf_buf(sb), sbuf_len(sb)); 696 | obj_end(); 697 | sbuf_free(sb); 698 | xobj[xobj_n++] = xobj_id; 699 | return xobj_n - 1; 700 | } 701 | 702 | void outpdf(char *pdf, int hwid, int vwid) 703 | { 704 | char buf[1 << 12]; 705 | struct sbuf *sb; 706 | int xobj_id; 707 | int fd, nr; 708 | /* reading the pdf file */ 709 | sb = sbuf_make(); 710 | fd = open(pdf, O_RDONLY); 711 | while ((nr = read(fd, buf, sizeof(buf))) > 0) 712 | sbuf_mem(sb, buf, nr); 713 | close(fd); 714 | /* the XObject */ 715 | xobj_id = pdfext(sbuf_buf(sb), sbuf_len(sb), hwid, vwid); 716 | sbuf_free(sb); 717 | o_flush(); 718 | out_fontup(); 719 | if (xobj_id >= 0) 720 | sbuf_printf(pg, "ET /FO%d Do BT\n", xobj_id); 721 | p_h = -1; 722 | p_v = -1; 723 | } 724 | 725 | void outlink(char *lnk, int hwid, int vwid) 726 | { 727 | if (ann_n == LEN(ann)) 728 | return; 729 | o_flush(); 730 | ann[ann_n++] = obj_beg(0); 731 | pdfout("<<\n"); 732 | pdfout(" /Type /Annot\n"); 733 | pdfout(" /Subtype /Link\n"); 734 | pdfout(" /Rect [%s", pdfpos(o_h, o_v)); 735 | pdfout(" %s]\n", pdfpos(o_h + hwid, o_v + vwid)); 736 | if (lnk[0] == '#') { /* internal links */ 737 | pdfout(" /A << /S /GoTo /D (%s) >>\n", lnk + 1); 738 | } else { /* external links */ 739 | pdfout(" /A << /S /URI /URI %s >>\n", pdftext_static(lnk)); 740 | } 741 | pdfout(">>\n"); 742 | obj_end(); 743 | } 744 | 745 | void outname(int n, char (*desc)[64], int *page, int *off) 746 | { 747 | int i; 748 | o_flush(); 749 | pdf_dests = obj_beg(0); 750 | pdfout("<<\n"); 751 | for (i = 0; i < n; i++) { 752 | if (page[i] > 0 && page[i] - 1 < page_n) 753 | pdfout(" /%s [ %d 0 R /XYZ 0 %d 0 ]\n", 754 | desc[i], page_id[page[i] - 1], 755 | pdf_height - (off[i] * 72 / dev_res)); 756 | } 757 | pdfout(">>\n"); 758 | obj_end(); 759 | } 760 | 761 | void outmark(int n, char (*desc)[256], int *page, int *off, int *level) 762 | { 763 | int *objs = malloc(n * sizeof(objs[0])); 764 | int i, j; 765 | int cnt = 0; 766 | /* allocating objects */ 767 | pdf_outline = obj_map(); 768 | for (i = 0; i < n; i++) 769 | objs[i] = obj_map(); 770 | o_flush(); 771 | /* root object */ 772 | obj_beg(pdf_outline); 773 | pdfout("<<\n"); 774 | for (i = 0; i < n; i++) 775 | if (level[i] == level[0]) 776 | cnt++; 777 | pdfout(" /Count %d\n", cnt); 778 | pdfout(" /First %d 0 R\n", objs[0]); 779 | for (i = n - 1; i > 0 && level[i] > level[0]; i--) 780 | ; 781 | pdfout(" /Last %d 0 R\n", objs[i]); 782 | pdfout(">>\n"); 783 | obj_end(); 784 | /* other objects */ 785 | for (i = 0; i < n; i++) { 786 | int cnt = 0; 787 | for (j = i + 1; j < n && level[j] > level[i]; j++) 788 | if (level[j] == level[i] + 1) 789 | cnt++; 790 | obj_beg(objs[i]); 791 | pdfout("<<\n"); 792 | pdfout(" /Title %s\n", pdftext_static(desc[i])); 793 | /* the parent field */ 794 | for (j = i - 1; j >= 0 && level[j] >= level[i]; j--) 795 | ; 796 | pdfout(" /Parent %d 0 R\n", j >= 0 ? objs[j] : pdf_outline); 797 | /* the next field */ 798 | for (j = i + 1; j < n && level[j] > level[i]; j++) 799 | ; 800 | if (j < n && level[j] == level[i]) 801 | pdfout(" /Next %d 0 R\n", objs[j]); 802 | /* the prev field */ 803 | for (j = i - 1; j >= 0 && level[j] > level[i]; j--) 804 | ; 805 | if (j >= 0 && level[j] == level[i]) 806 | pdfout(" /Prev %d 0 R\n", objs[j]); 807 | /* node children */ 808 | if (cnt) { 809 | int last = 0; 810 | pdfout(" /Count %d\n", cnt); 811 | pdfout(" /First %d 0 R\n", objs[i + 1]); 812 | for (j = i + 1; j < n && level[j] > level[i]; j++) 813 | if (level[j] == level[i] + 1) 814 | last = j; 815 | pdfout(" /Last %d 0 R\n", objs[last]); 816 | } 817 | if (page[i] > 0 && page[i] - 1 < page_n) 818 | pdfout(" /Dest [ %d 0 R /XYZ 0 %d 0 ]\n", 819 | page_id[page[i] - 1], 820 | pdf_height - (off[i] * 72 / dev_res)); 821 | pdfout(">>\n"); 822 | obj_end(); 823 | } 824 | free(objs); 825 | } 826 | 827 | void outinfo(char *kwd, char *val) 828 | { 829 | if (!strcmp("Author", kwd)) 830 | snprintf(pdf_author, sizeof(pdf_author), "%s", val); 831 | if (!strcmp("Title", kwd)) 832 | snprintf(pdf_title, sizeof(pdf_title), "%s", val); 833 | } 834 | 835 | void outset(char *var, char *val) 836 | { 837 | if (!strcmp("linewidth", var)) 838 | pdf_linewid = atoi(val); 839 | if (!strcmp("linecap", var)) 840 | pdf_linecap = atoi(val); 841 | if (!strcmp("linejoin", var)) 842 | pdf_linejoin = atoi(val); 843 | } 844 | 845 | void outpage(void) 846 | { 847 | o_v = 0; 848 | o_h = 0; 849 | p_i = 0; 850 | p_v = 0; 851 | p_h = 0; 852 | p_s = 0; 853 | p_f = 0; 854 | p_m = 0; 855 | o_i = -1; 856 | } 857 | 858 | void outmnt(int f) 859 | { 860 | if (p_f == f) 861 | p_f = -1; 862 | } 863 | 864 | void outgname(int g) 865 | { 866 | } 867 | 868 | void drawbeg(void) 869 | { 870 | o_flush(); 871 | out_fontup(); 872 | sbuf_printf(pg, "%s m\n", pdfpos(o_h, o_v)); 873 | } 874 | 875 | static int l_page, l_size, l_wid, l_cap, l_join; /* drawing line properties */ 876 | 877 | void drawend(int close, int fill) 878 | { 879 | fill = !fill ? 2 : fill; 880 | if (l_page != page_n || l_size != o_s || l_wid != pdf_linewid || 881 | l_cap != pdf_linecap || l_join != pdf_linejoin) { 882 | int lwid = pdf_linewid * o_s; 883 | sbuf_printf(pg, "%d.%03d w\n", lwid / 1000, lwid % 1000); 884 | sbuf_printf(pg, "%d J %d j\n", pdf_linecap, pdf_linejoin); 885 | l_page = page_n; 886 | l_size = o_s; 887 | l_wid = pdf_linewid; 888 | l_cap = pdf_linecap; 889 | l_join = pdf_linejoin; 890 | } 891 | if (fill & 2) /* stroking color */ 892 | sbuf_printf(pg, "%s RG\n", pdfcolor(o_m)); 893 | if (fill & 1) 894 | sbuf_printf(pg, (fill & 2) ? "b\n" : "f\n"); 895 | else 896 | sbuf_printf(pg, close ? "s\n" : "S\n"); 897 | p_v = 0; 898 | p_h = 0; 899 | } 900 | 901 | void drawmbeg(char *s) 902 | { 903 | } 904 | 905 | void drawmend(char *s) 906 | { 907 | } 908 | 909 | void drawl(int h, int v) 910 | { 911 | outrel(h, v); 912 | sbuf_printf(pg, "%s l\n", pdfpos(o_h, o_v)); 913 | } 914 | 915 | /* draw circle/ellipse quadrant */ 916 | static void drawquad(int ch, int cv) 917 | { 918 | long long b = 551915; 919 | long long x0 = o_h * 1000; 920 | long long y0 = o_v * 1000; 921 | long long x3 = x0 + ch * 1000 / 2; 922 | long long y3 = y0 + cv * 1000 / 2; 923 | long long x1 = x0; 924 | long long y1 = y0 + cv * b / 1000 / 2; 925 | long long x2 = x3 - ch * b / 1000 / 2; 926 | long long y2 = y3; 927 | if ((ch > 0 && cv < 0) || (ch < 0 && cv > 0)) { 928 | x1 = x0 + ch * b / 1000 / 2; 929 | y1 = y0; 930 | x2 = x3; 931 | y2 = y3 - cv * b / 1000 / 2; 932 | } 933 | sbuf_printf(pg, "%s ", pdfpos00(x1 / 10, y1 / 10)); 934 | sbuf_printf(pg, "%s ", pdfpos00(x2 / 10, y2 / 10)); 935 | sbuf_printf(pg, "%s c\n", pdfpos00(x3 / 10, y3 / 10)); 936 | outrel(ch / 2, cv / 2); 937 | } 938 | 939 | /* draw a circle */ 940 | void drawc(int c) 941 | { 942 | drawquad(+c, +c); 943 | drawquad(+c, -c); 944 | drawquad(-c, -c); 945 | drawquad(-c, +c); 946 | outrel(c, 0); 947 | } 948 | 949 | /* draw an ellipse */ 950 | void drawe(int h, int v) 951 | { 952 | drawquad(+h, +v); 953 | drawquad(+h, -v); 954 | drawquad(-h, -v); 955 | drawquad(-h, +v); 956 | outrel(h, 0); 957 | } 958 | 959 | /* draw an arc */ 960 | void drawa(int h1, int v1, int h2, int v2) 961 | { 962 | drawl(h1 + h2, v1 + v2); 963 | } 964 | 965 | /* draw a spline */ 966 | void draws(int h1, int v1, int h2, int v2) 967 | { 968 | int x0 = o_h; 969 | int y0 = o_v; 970 | int x1 = x0 + h1; 971 | int y1 = y0 + v1; 972 | int x2 = x1 + h2; 973 | int y2 = y1 + v2; 974 | 975 | sbuf_printf(pg, "%s ", pdfpos((x0 + 5 * x1) / 6, (y0 + 5 * y1) / 6)); 976 | sbuf_printf(pg, "%s ", pdfpos((x2 + 5 * x1) / 6, (y2 + 5 * y1) / 6)); 977 | sbuf_printf(pg, "%s c\n", pdfpos((x1 + x2) / 2, (y1 + y2) / 2)); 978 | 979 | outrel(h1, v1); 980 | } 981 | 982 | void docheader(char *title, int pagewidth, int pageheight, int linewidth) 983 | { 984 | if (title) 985 | outinfo("Title", title); 986 | obj_map(); 987 | pdf_root = obj_map(); 988 | pdf_pages = obj_map(); 989 | pdfout("%%PDF-1.6\n\n"); 990 | pdf_width = (pagewidth * 72 + 127) / 254; 991 | pdf_height = (pageheight * 72 + 127) / 254; 992 | pdf_linewid = linewidth; 993 | } 994 | 995 | void doctrailer(int pages) 996 | { 997 | int i; 998 | int xref_off; 999 | int info_id; 1000 | /* pdf pages object */ 1001 | obj_beg(pdf_pages); 1002 | pdfout("<<\n"); 1003 | pdfout(" /Type /Pages\n"); 1004 | pdfout(" /MediaBox [ 0 0 %d %d ]\n", pdf_width, pdf_height); 1005 | pdfout(" /Count %d\n", page_n); 1006 | pdfout(" /Kids ["); 1007 | for (i = 0; i < page_n; i++) 1008 | pdfout(" %d 0 R", page_id[i]); 1009 | pdfout(" ]\n"); 1010 | pdfout(">>\n"); 1011 | obj_end(); 1012 | /* pdf root object */ 1013 | obj_beg(pdf_root); 1014 | pdfout("<<\n"); 1015 | pdfout(" /Type /Catalog\n"); 1016 | pdfout(" /Pages %d 0 R\n", pdf_pages); 1017 | if (pdf_dests > 0) 1018 | pdfout(" /Dests %d 0 R\n", pdf_dests); 1019 | if (pdf_outline > 0) 1020 | pdfout(" /Outlines %d 0 R\n", pdf_outline); 1021 | pdfout(">>\n"); 1022 | obj_end(); 1023 | /* fonts */ 1024 | pfont_done(); 1025 | /* info object */ 1026 | info_id = obj_beg(0); 1027 | pdfout("<<\n"); 1028 | if (pdf_title[0]) 1029 | pdfout(" /Title %s\n", pdftext_static(pdf_title)); 1030 | if (pdf_author[0]) 1031 | pdfout(" /Author %s\n", pdftext_static(pdf_author)); 1032 | pdfout(" /Creator (Neatroff)\n"); 1033 | pdfout(" /Producer (Neatpost)\n"); 1034 | pdfout(">>\n"); 1035 | obj_end(); 1036 | /* the xref */ 1037 | xref_off = pdf_pos; 1038 | pdfout("xref\n"); 1039 | pdfout("0 %d\n", obj_n); 1040 | pdfout("0000000000 65535 f \n"); 1041 | for (i = 1; i < obj_n; i++) 1042 | pdfout("%010d 00000 n \n", obj_off[i]); 1043 | /* the trailer */ 1044 | pdfout("trailer\n"); 1045 | pdfout("<<\n"); 1046 | pdfout(" /Size %d\n", obj_n); 1047 | pdfout(" /Root %d 0 R\n", pdf_root); 1048 | pdfout(" /Info %d 0 R\n", info_id); 1049 | pdfout(">>\n"); 1050 | pdfout("startxref\n"); 1051 | pdfout("%d\n", xref_off); 1052 | pdfout("%%%%EOF\n"); 1053 | free(page_id); 1054 | free(obj_off); 1055 | } 1056 | 1057 | void docpagebeg(int n) 1058 | { 1059 | pg = sbuf_make(); 1060 | sbuf_printf(pg, "BT\n"); 1061 | } 1062 | 1063 | void docpageend(int n) 1064 | { 1065 | int cont_id; 1066 | int i; 1067 | o_flush(); 1068 | sbuf_printf(pg, "ET\n"); 1069 | /* page contents */ 1070 | cont_id = obj_beg(0); 1071 | pdfout("<<\n"); 1072 | pdfout(" /Length %d\n", sbuf_len(pg) - 1); 1073 | pdfout(">>\n"); 1074 | pdfout("stream\n"); 1075 | pdfmem(sbuf_buf(pg), sbuf_len(pg)); 1076 | pdfout("endstream\n"); 1077 | obj_end(); 1078 | /* the page object */ 1079 | if (page_n == page_sz) { 1080 | page_sz += 1024; 1081 | page_id = mextend(page_id, page_n, page_sz, sizeof(page_id[0])); 1082 | } 1083 | page_id[page_n++] = obj_beg(0); 1084 | pdfout("<<\n"); 1085 | pdfout(" /Type /Page\n"); 1086 | pdfout(" /Parent %d 0 R\n", pdf_pages); 1087 | pdfout(" /Resources <<\n"); 1088 | pdfout(" /Font <<"); 1089 | for (i = 0; i < pfonts_n; i++) { 1090 | if (o_iset[i]) { 1091 | struct pfont *ps = &pfonts[i]; 1092 | if (ps->cid) 1093 | pdfout(" /%s %d 0 R", ps->name, ps->obj); 1094 | else 1095 | pdfout(" /%s.%d %d 0 R", ps->name, ps->sub, ps->obj); 1096 | } 1097 | } 1098 | pdfout(" >>\n"); 1099 | if (xobj_n) { /* XObjects */ 1100 | pdfout(" /XObject <<"); 1101 | for (i = 0; i < xobj_n; i++) 1102 | pdfout(" /FO%d %d 0 R", i, xobj[i]); 1103 | pdfout(" >>\n"); 1104 | } 1105 | pdfout(" >>\n"); 1106 | pdfout(" /Contents %d 0 R\n", cont_id); 1107 | if (ann_n) { 1108 | pdfout(" /Annots ["); 1109 | for (i = 0; i < ann_n; i++) 1110 | pdfout(" %d 0 R", ann[i]); 1111 | pdfout(" ]\n"); 1112 | } 1113 | pdfout(">>\n"); 1114 | obj_end(); 1115 | sbuf_free(pg); 1116 | memset(o_iset, 0, pfonts_n * sizeof(o_iset[0])); 1117 | xobj_n = 0; 1118 | ann_n = 0; 1119 | } 1120 | -------------------------------------------------------------------------------- /pdfext.c: -------------------------------------------------------------------------------- 1 | /* Parse and extract PDF objects */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "post.h" 7 | 8 | /* the number white space characters */ 9 | int pdf_ws(char *pdf, int len, int pos) 10 | { 11 | int i = pos; 12 | while (i < len && isspace((unsigned char) pdf[i])) 13 | i++; 14 | return i - pos; 15 | } 16 | 17 | /* s: string, d: dictionary, l: list, n: number, /: name, r: reference */ 18 | int pdf_type(char *pdf, int len, int pos) 19 | { 20 | pos += pdf_ws(pdf, len, pos); 21 | if (pdf[pos] == '/') 22 | return '/'; 23 | if (pdf[pos] == '(') 24 | return 's'; 25 | if (pdf[pos] == '<' && pdf[pos + 1] != '<') 26 | return 's'; 27 | if (pdf[pos] == '<' && pdf[pos + 1] == '<') 28 | return 'd'; 29 | if (pdf[pos] == '[') 30 | return 'l'; 31 | if (strchr("0123456789+-.", (unsigned char) pdf[pos])) { 32 | if (!isdigit((unsigned char) pdf[pos])) 33 | return 'n'; 34 | while (pos < len && isdigit((unsigned char) pdf[pos])) 35 | pos++; 36 | pos += pdf_ws(pdf, len, pos); 37 | if (!isdigit((unsigned char) pdf[pos])) 38 | return 'n'; 39 | while (pos < len && isdigit((unsigned char) pdf[pos])) 40 | pos++; 41 | pos += pdf_ws(pdf, len, pos); 42 | return pos < len && pdf[pos] == 'R' ? 'r' : 'n'; 43 | } 44 | return -1; 45 | } 46 | 47 | /* the length of a pdf object */ 48 | int pdf_len(char *pdf, int len, int pos) 49 | { 50 | int c; 51 | int old = pos; 52 | if (pos >= len) 53 | return 0; 54 | pos += pdf_ws(pdf, len, pos); 55 | c = (unsigned char) pdf[pos]; 56 | if (strchr("0123456789+-.", c)) { 57 | if (pdf_type(pdf, len, pos) == 'r') { 58 | char *r = memchr(pdf + pos, 'R', len - pos); 59 | return r - (pdf + old) + 1; 60 | } 61 | pos++; 62 | while (pos < len && strchr("0123456789.", (unsigned char) pdf[pos])) 63 | pos++; 64 | } 65 | if (c == '(') { 66 | int depth = 1; 67 | pos++; 68 | while (pos < len && depth > 0) { 69 | if (pdf[pos] == '(') 70 | depth++; 71 | if (pdf[pos] == ')') 72 | depth--; 73 | if (pdf[pos] == '\\') 74 | pos++; 75 | pos++; 76 | } 77 | } 78 | if (c == '<' && pos + 1 < len && pdf[pos + 1] == '<') { 79 | pos += 2; 80 | while (pos + 2 < len && (pdf[pos] != '>' || pdf[pos + 1] != '>')) { 81 | pos += pdf_len(pdf, len, pos); 82 | pos += pdf_len(pdf, len, pos); 83 | pos += pdf_ws(pdf, len, pos); 84 | } 85 | if (pos + 2 < len) 86 | pos += 2; 87 | } else if (c == '<') { 88 | while (pos < len && pdf[pos] != '>') 89 | pos++; 90 | if (pos < len) 91 | pos++; 92 | } 93 | if (c == '/') { 94 | pos++; 95 | while (pos < len && !strchr(" \t\r\n\f()<>[]{}/%", 96 | (unsigned char) pdf[pos])) 97 | pos++; 98 | } 99 | if (c == '[') { 100 | pos++; 101 | while (pos < len && pdf[pos] != ']') { 102 | pos += pdf_len(pdf, len, pos); 103 | pos += pdf_ws(pdf, len, pos); 104 | } 105 | pos++; 106 | } 107 | return pos - old; 108 | } 109 | 110 | static int startswith(char *s, char *t) 111 | { 112 | while (*s && *t) 113 | if (*s++ != *t++) 114 | return 0; 115 | return 1; 116 | } 117 | 118 | /* read an indirect reference */ 119 | int pdf_obj(char *pdf, int len, int pos, int *obj, int *rev) 120 | { 121 | if (pdf_type(pdf, len, pos) != 'r') 122 | return -1; 123 | *obj = atoi(pdf + pos); 124 | pos += pdf_len(pdf, len, pos); 125 | *rev = atoi(pdf + pos); 126 | return 0; 127 | } 128 | 129 | /* the value of a pdf dictionary key */ 130 | int pdf_dval(char *pdf, int len, int pos, char *key) 131 | { 132 | pos += 2; 133 | while (pos + 2 < len && (pdf[pos] != '>' || pdf[pos + 1] != '>')) { 134 | pos += pdf_ws(pdf, len, pos); 135 | if (pdf_len(pdf, len, pos) == strlen(key) && startswith(key, pdf + pos)) { 136 | pos += pdf_len(pdf, len, pos); 137 | pos += pdf_ws(pdf, len, pos); 138 | return pos; 139 | } 140 | pos += pdf_len(pdf, len, pos); 141 | pos += pdf_len(pdf, len, pos); 142 | pos += pdf_ws(pdf, len, pos); 143 | } 144 | return -1; 145 | } 146 | 147 | /* return a dictionary key */ 148 | int pdf_dkey(char *pdf, int len, int pos, int key) 149 | { 150 | int i = 0; 151 | pos += 2; 152 | while (pos + 2 < len && (pdf[pos] != '>' || pdf[pos + 1] != '>')) { 153 | pos += pdf_ws(pdf, len, pos); 154 | if (i++ == key) 155 | return pos; 156 | pos += pdf_len(pdf, len, pos); 157 | pos += pdf_len(pdf, len, pos); 158 | pos += pdf_ws(pdf, len, pos); 159 | } 160 | return -1; 161 | } 162 | 163 | /* return a list entry */ 164 | int pdf_lval(char *pdf, int len, int pos, int idx) 165 | { 166 | int i = 0; 167 | pos++; 168 | while (pos < len && pdf[pos] != ']') { 169 | if (i++ == idx) 170 | return pos; 171 | pos += pdf_len(pdf, len, pos); 172 | pos += pdf_ws(pdf, len, pos); 173 | } 174 | return -1; 175 | } 176 | 177 | static void *my_memrchr(void *m, int c, long n) 178 | { 179 | int i; 180 | for (i = 0; i < n; i++) 181 | if (*(unsigned char *) (m + n - 1 - i) == c) 182 | return m + n - 1 - i; 183 | return NULL; 184 | } 185 | 186 | static int prevline(char *pdf, int len, int off) 187 | { 188 | char *nl = my_memrchr(pdf, '\n', off); 189 | if (nl && nl > pdf) { 190 | char *nl2 = my_memrchr(pdf, '\n', nl - pdf - 1); 191 | if (nl2) 192 | return nl2 - pdf + 1; 193 | } 194 | return -1; 195 | } 196 | 197 | static int nextline(char *pdf, int len, int off) 198 | { 199 | char *nl = memchr(pdf + off, '\n', len - off); 200 | if (nl) 201 | return nl - pdf + 1; 202 | return -1; 203 | } 204 | 205 | /* the position of the trailer */ 206 | int pdf_trailer(char *pdf, int len) 207 | { 208 | int pos = prevline(pdf, len, len); /* %%EOF */ 209 | while (!startswith(pdf + pos, "trailer")) 210 | if ((pos = prevline(pdf, len, pos)) < 0) 211 | return -1; 212 | return nextline(pdf, len, pos); /* skip trailer\n */ 213 | } 214 | 215 | /* the position of the last xref table */ 216 | static int pdf_xref(char *pdf, int len) 217 | { 218 | int pos = prevline(pdf, len, len); /* %%EOF */ 219 | if ((pos = prevline(pdf, len, pos)) < 0) 220 | return -1; 221 | /* read startxref offset */ 222 | if (sscanf(pdf + pos, "%d", &pos) != 1 || pos >= len || pos < 0) 223 | return -1; 224 | return nextline(pdf, len, pos); /* skip xref\n */ 225 | } 226 | 227 | /* find a pdf object */ 228 | int pdf_find(char *pdf, int len, int obj, int rev) 229 | { 230 | int obj_beg, obj_cnt; 231 | int cur_rev, cur_pos; 232 | char *beg; 233 | int i; 234 | int pos = pdf_xref(pdf, len); 235 | if (pos < 0) 236 | return -1; 237 | /* the numbers after xref */ 238 | while (pos < len && sscanf(pdf + pos, "%d %d", &obj_beg, &obj_cnt) == 2) { 239 | for (i = 0; i < obj_cnt; i++) { 240 | if ((pos = nextline(pdf, len, pos)) < 0) 241 | return -1; 242 | if (sscanf(pdf + pos, "%d %d", &cur_pos, &cur_rev) != 2) 243 | return -1; 244 | if (obj_beg + i == obj && cur_rev == rev) { 245 | if (cur_pos < 0 || cur_pos >= len) 246 | return -1; 247 | if (!(beg = strstr(pdf + cur_pos, "obj"))) 248 | return -1; 249 | pos = beg - pdf + 3; 250 | pos += pdf_ws(pdf, len, pos); 251 | return pos; 252 | } 253 | } 254 | } 255 | return -1; 256 | } 257 | 258 | /* read and dereference an indirect reference */ 259 | int pdf_ref(char *pdf, int len, int pos) 260 | { 261 | int obj, rev; 262 | if (pdf_obj(pdf, len, pos, &obj, &rev)) 263 | return -1; 264 | return pdf_find(pdf, len, obj, rev); 265 | } 266 | 267 | /* retrieve and dereference a dictionary entry */ 268 | int pdf_dval_val(char *pdf, int len, int pos, char *key) 269 | { 270 | int val = pdf_dval(pdf, len, pos, key); 271 | int val_obj, val_rev; 272 | if (val < 0) 273 | return -1; 274 | if (pdf_type(pdf, len, val) == 'r') { 275 | pdf_obj(pdf, len, val, &val_obj, &val_rev); 276 | return pdf_find(pdf, len, val_obj, val_rev); 277 | } 278 | return val; 279 | } 280 | 281 | /* retrieve a dictionary entry, which is an indirect reference */ 282 | int pdf_dval_obj(char *pdf, int len, int pos, char *key) 283 | { 284 | int val = pdf_dval(pdf, len, pos, key); 285 | if (val < 0) 286 | return -1; 287 | return pdf_ref(pdf, len, val); 288 | } 289 | -------------------------------------------------------------------------------- /post.c: -------------------------------------------------------------------------------- 1 | /* 2 | * NEATPOST: NEATROFF'S POSTSCRIPT/PDF POSTPROCESSOR 3 | * 4 | * Copyright (C) 2013-2025 Ali Gholami Rudi 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "post.h" 23 | 24 | static char *ps_title; /* document title */ 25 | static int ps_pagewidth = 2159; /* page width (tenths of a millimetre) */ 26 | static int ps_pageheight = 2794;/* page height (tenths of a millimetre) */ 27 | static int ps_linewidth = 40; /* drawing line thickness in thousandths of an em */ 28 | static int o_pages; /* output pages */ 29 | 30 | /* bookmarks */ 31 | static char (*mark_desc)[256]; /* bookmark description */ 32 | static int *mark_page; /* bookmark page */ 33 | static int *mark_offset; /* bookmark offset */ 34 | static int *mark_level; /* bookmark level */ 35 | static int mark_n; /* number of bookmarks */ 36 | static int mark_sz; /* allocated size of bookmark arrays */ 37 | 38 | /* named destinations */ 39 | static char (*name_desc)[64]; /* reference name */ 40 | static int *name_page; /* reference page */ 41 | static int *name_offset; /* reference offset */ 42 | static int name_n; /* number of references */ 43 | static int name_sz; /* allocated size of name arrays */ 44 | 45 | static int next(void) 46 | { 47 | return getc(stdin); 48 | } 49 | 50 | static void back(int c) 51 | { 52 | ungetc(c, stdin); 53 | } 54 | 55 | static int utf8len(int c) 56 | { 57 | if (~c & 0xc0) /* ASCII or invalid */ 58 | return c > 0; 59 | if (~c & 0x20) 60 | return 2; 61 | if (~c & 0x10) 62 | return 3; 63 | if (~c & 0x08) 64 | return 4; 65 | return 1; 66 | } 67 | 68 | static int nextutf8(char *s) 69 | { 70 | int c = next(); 71 | int l = utf8len(c); 72 | int i; 73 | if (c < 0) 74 | return 0; 75 | s[0] = c; 76 | for (i = 1; i < l; i++) 77 | s[i] = next(); 78 | s[l] = '\0'; 79 | return l; 80 | } 81 | 82 | /* skip blanks */ 83 | static void nextskip(void) 84 | { 85 | int c; 86 | do { 87 | c = next(); 88 | } while (isspace(c)); 89 | back(c); 90 | } 91 | 92 | static int nextnum(void) 93 | { 94 | int c; 95 | int n = 0; 96 | int neg = 0; 97 | nextskip(); 98 | while (1) { 99 | c = next(); 100 | if (!n && (c == '-' || c == '+')) { 101 | neg = c == '-'; 102 | continue; 103 | } 104 | if (!isdigit(c)) 105 | back(c); 106 | if (c < 0 || !isdigit(c)) 107 | break; 108 | n = n * 10 + c - '0'; 109 | } 110 | return neg ? -n : n; 111 | } 112 | 113 | static int readnum(int *n) 114 | { 115 | int c; 116 | do { 117 | c = next(); 118 | } while (c == ' '); 119 | back(c); 120 | if (c == '-' || c == '+' || (c >= '0' && c <= '9')) { 121 | *n = nextnum(); 122 | return 0; 123 | } 124 | return 1; 125 | } 126 | 127 | static int iseol(void) 128 | { 129 | int c; 130 | do { 131 | c = next(); 132 | } while (c == ' '); 133 | back(c); 134 | return c == '\n'; 135 | } 136 | 137 | /* skip until the end of line */ 138 | static void nexteol(void) 139 | { 140 | int c; 141 | do { 142 | c = next(); 143 | } while (c >= 0 && c != '\n'); 144 | } 145 | 146 | static void nextword(char *s) 147 | { 148 | int c; 149 | nextskip(); 150 | c = next(); 151 | while (c >= 0 && !isspace(c)) { 152 | *s++ = c; 153 | c = next(); 154 | } 155 | if (c >= 0) 156 | back(c); 157 | *s = '\0'; 158 | } 159 | 160 | /* read until eol */ 161 | static void readln(char *s) 162 | { 163 | int c; 164 | c = next(); 165 | while (c > 0 && c != '\n') { 166 | *s++ = c; 167 | c = next(); 168 | } 169 | if (c == '\n') 170 | back(c); 171 | *s = '\0'; 172 | } 173 | 174 | static void postline(void) 175 | { 176 | int h, v; 177 | while (!readnum(&h) && !readnum(&v)) 178 | drawl(h, v); 179 | } 180 | 181 | static void postarc(void) 182 | { 183 | int h1, v1, h2, v2; 184 | if (!readnum(&h1) && !readnum(&v1) && !readnum(&h2) && !readnum(&v2)) 185 | drawa(h1, v1, h2, v2); 186 | } 187 | 188 | static void postspline(void) 189 | { 190 | int h2, v2; 191 | int h1 = nextnum(); 192 | int v1 = nextnum(); 193 | if (iseol()) { 194 | drawl(h1, v1); 195 | return; 196 | } 197 | while (!readnum(&h2) && !readnum(&v2)) { 198 | draws(h1, v1, h2, v2); 199 | h1 = h2; 200 | v1 = v2; 201 | } 202 | draws(h1, v1, 0, 0); 203 | } 204 | 205 | static void postpoly(void) 206 | { 207 | int l = 'l'; 208 | int c; 209 | while (!iseol() && (l == 'l' || l == '~' || l == 'a')) { 210 | do { 211 | c = next(); 212 | } while (c == ' '); 213 | back(c); 214 | if (c != '-' && c != '+' && (c < '0' || c > '9')) { 215 | l = c; 216 | while (c >= 0 && !isspace(c)) 217 | c = next(); 218 | continue; 219 | } 220 | if (l == 'l') 221 | postline(); 222 | if (l == '~') 223 | postspline(); 224 | if (l == 'a') 225 | postarc(); 226 | } 227 | } 228 | 229 | static void postdraw(void) 230 | { 231 | int h1, v1; 232 | int c = next(); 233 | drawbeg(); 234 | switch (tolower(c)) { 235 | case 'l': 236 | h1 = nextnum(); 237 | v1 = nextnum(); 238 | drawl(h1, v1); 239 | break; 240 | case 'c': 241 | drawc(nextnum()); 242 | break; 243 | case 'e': 244 | h1 = nextnum(); 245 | v1 = nextnum(); 246 | drawe(h1, v1); 247 | break; 248 | case 'a': 249 | postarc(); 250 | break; 251 | case '~': 252 | postspline(); 253 | break; 254 | case 'p': 255 | postpoly(); 256 | break; 257 | } 258 | drawend(c == 'p' || c == 'P', c == 'E' || c == 'C' || c == 'P'); 259 | nexteol(); 260 | } 261 | 262 | static char *strcut(char *dst, char *src) 263 | { 264 | while (*src == ' ' || *src == '\n') 265 | src++; 266 | if (src[0] == '"') { 267 | src++; 268 | while (*src && (src[0] != '"' || src[1] == '"')) { 269 | if (*src == '"') 270 | src++; 271 | *dst++ = *src++; 272 | } 273 | if (*src == '"') 274 | src++; 275 | } else { 276 | while (*src && *src != ' ' && *src != '\n') 277 | *dst++ = *src++; 278 | } 279 | *dst = '\0'; 280 | return src; 281 | } 282 | 283 | static void postps(void) 284 | { 285 | char cmd[ILNLEN]; 286 | char arg[ILNLEN]; 287 | nextword(cmd); 288 | readln(arg); 289 | if (!strcmp("PS", cmd) || !strcmp("ps", cmd)) 290 | out("%s\n", arg); 291 | if (!strcmp("rotate", cmd)) 292 | outrotate(atoi(arg)); 293 | if (!strcmp("eps", cmd) || !strcmp("pdf", cmd)) { 294 | char path[1 << 12]; 295 | int hwid, vwid, nspec; 296 | char *spec = arg; 297 | spec = strcut(path, spec); 298 | nspec = sscanf(spec, "%d %d", &hwid, &vwid); 299 | if (nspec < 1) 300 | hwid = 0; 301 | if (nspec < 2) 302 | vwid = 0; 303 | if (path[0] && !strcmp("eps", cmd)) 304 | outeps(path, hwid, vwid); 305 | if (path[0] && !strcmp("pdf", cmd)) 306 | outpdf(path, hwid, vwid); 307 | } 308 | if (!strcmp("name", cmd)) { 309 | char *spec = arg; 310 | int nspec; 311 | if (name_n == name_sz) { 312 | name_sz = name_sz == 0 ? 128 : name_sz * 2; 313 | name_desc = mextend(name_desc, name_n, name_sz, sizeof(name_desc[0])); 314 | name_page = mextend(name_page, name_n, name_sz, sizeof(name_page[0])); 315 | name_offset = mextend(name_offset, name_n, name_sz, sizeof(name_offset[0])); 316 | } 317 | spec = strcut(name_desc[name_n], spec); 318 | nspec = sscanf(spec, "%d %d", &name_page[name_n], &name_offset[name_n]); 319 | if (name_desc[name_n][0] && nspec > 0) 320 | name_n++; 321 | } 322 | if (!strcmp("mark", cmd)) { 323 | char *spec = arg; 324 | int nspec; 325 | if (mark_n == mark_sz) { 326 | mark_sz = mark_sz == 0 ? 128 : mark_sz * 2; 327 | mark_desc = mextend(mark_desc, mark_n, mark_sz, sizeof(mark_desc[0])); 328 | mark_page = mextend(mark_page, mark_n, mark_sz, sizeof(mark_page[0])); 329 | mark_offset = mextend(mark_offset, mark_n, mark_sz, sizeof(mark_offset[0])); 330 | mark_level = mextend(mark_level, mark_n, mark_sz, sizeof(mark_level[0])); 331 | } 332 | spec = strcut(mark_desc[mark_n], spec); 333 | nspec = sscanf(spec, "%d %d %d", &mark_page[mark_n], 334 | &mark_offset[mark_n], &mark_level[mark_n]); 335 | if (mark_desc[mark_n][0] && nspec > 0) 336 | mark_n++; 337 | } 338 | if (!strcmp("link", cmd)) { 339 | char link[1 << 12]; 340 | int hwid, vwid, nspec; 341 | char *spec = arg; 342 | spec = strcut(link, spec); 343 | nspec = sscanf(spec, "%d %d", &hwid, &vwid); 344 | if (link[0] && nspec == 2) 345 | outlink(link, hwid, vwid); 346 | } 347 | if (!strcmp("info", cmd)) { 348 | char *spec = arg; 349 | char kwd[128]; 350 | int i = 0; 351 | while (*spec == ' ') 352 | spec++; 353 | while (*spec && *spec != ' ') { 354 | if (i < sizeof(kwd) - 1) 355 | kwd[i++] = *spec; 356 | spec++; 357 | } 358 | kwd[i] = '\0'; 359 | while (*spec == ' ') 360 | spec++; 361 | outinfo(kwd, spec); 362 | } 363 | if (!strcmp("set", cmd)) { 364 | char var[128]; 365 | char val[128]; 366 | if (sscanf(arg, "%128s %128s", var, val) == 2) 367 | outset(var, val); 368 | } 369 | if (!strcmp("BeginObject", cmd)) 370 | drawmbeg(arg); 371 | if (!strcmp("EndObject", cmd)) 372 | drawmend(arg); 373 | } 374 | 375 | static char postdir[PATHLEN] = TROFFFDIR; /* output device directory */ 376 | static char postdev[PATHLEN] = "utf"; /* output device name */ 377 | 378 | static void postx(void) 379 | { 380 | char cmd[128]; 381 | char font[128]; 382 | int pos; 383 | nextword(cmd); 384 | switch (cmd[0]) { 385 | case 'f': 386 | pos = nextnum(); 387 | nextword(font); 388 | dev_mnt(pos, font, font); 389 | outmnt(pos); 390 | break; 391 | case 'i': 392 | if (dev_open(postdir, postdev)) { 393 | fprintf(stderr, "neatpost: cannot open device %s\n", postdev); 394 | exit(1); 395 | } 396 | docheader(ps_title, ps_pagewidth, ps_pageheight, ps_linewidth); 397 | break; 398 | case 'T': 399 | nextword(postdev); 400 | break; 401 | case 's': 402 | break; 403 | case 'X': 404 | postps(); 405 | break; 406 | } 407 | nexteol(); 408 | } 409 | 410 | static void postcmd(int c) 411 | { 412 | char cs[GNLEN]; 413 | if (isdigit(c)) { 414 | outrel((c - '0') * 10 + next() - '0', 0); 415 | nextutf8(cs); 416 | outc(cs); 417 | return; 418 | } 419 | switch (c) { 420 | case 's': 421 | outsize(nextnum()); 422 | break; 423 | case 'f': 424 | outfont(nextnum()); 425 | break; 426 | case 'H': 427 | outh(nextnum()); 428 | break; 429 | case 'V': 430 | outv(nextnum()); 431 | break; 432 | case 'h': 433 | outrel(nextnum(), 0); 434 | break; 435 | case 'v': 436 | outrel(0, nextnum()); 437 | break; 438 | case 'c': 439 | nextutf8(cs); 440 | outc(cs); 441 | break; 442 | case 'm': 443 | nextword(cs); 444 | outcolor(clr_get(cs)); 445 | break; 446 | case 'N': 447 | nextnum(); 448 | break; 449 | case 'C': 450 | nextword(cs); 451 | outc(cs); 452 | break; 453 | case 'p': 454 | if (o_pages) 455 | docpageend(o_pages); 456 | o_pages = nextnum(); 457 | docpagebeg(o_pages); 458 | outpage(); 459 | break; 460 | case 'w': 461 | break; 462 | case 'n': 463 | nextnum(); 464 | nextnum(); 465 | break; 466 | case 'D': 467 | postdraw(); 468 | break; 469 | case 'x': 470 | postx(); 471 | break; 472 | case '#': 473 | nexteol(); 474 | break; 475 | default: 476 | fprintf(stderr, "neatpost: unknown command %c\n", c); 477 | nexteol(); 478 | } 479 | } 480 | 481 | static void post(void) 482 | { 483 | int c; 484 | while ((c = next()) >= 0) 485 | if (!isspace(c)) 486 | postcmd(c); 487 | if (o_pages) 488 | docpageend(o_pages); 489 | if (name_n) 490 | outname(name_n, name_desc, name_page, name_offset); 491 | if (mark_n) 492 | outmark(mark_n, mark_desc, mark_page, mark_offset, mark_level); 493 | } 494 | 495 | static struct paper { 496 | char *name; 497 | int w, h; 498 | } papers[] = { 499 | {"letter", 2159, 2794}, 500 | {"legal", 2159, 3556}, 501 | {"ledger", 4318, 2794}, 502 | {"tabloid", 2794, 4318}, 503 | }; 504 | 505 | static void setpagesize(char *s) 506 | { 507 | int d1, d2, n, i; 508 | /* predefined paper sizes */ 509 | for (i = 0; i < LEN(papers); i++) { 510 | if (!strcmp(papers[i].name, s)) { 511 | ps_pagewidth = papers[i].w; 512 | ps_pageheight = papers[i].h; 513 | return; 514 | } 515 | } 516 | /* custom paper size in tenth of mm; example: 2100x2970 for a4 */ 517 | if (isdigit(s[0]) && strchr(s, 'x')) { 518 | ps_pagewidth = atoi(s); 519 | ps_pageheight = atoi(strchr(s, 'x') + 1); 520 | return; 521 | } 522 | /* ISO paper sizes */ 523 | if (!strchr("abcABC", s[0]) || !isdigit(s[1])) 524 | return; 525 | if (tolower(s[0]) == 'a') { 526 | d1 = 8410; 527 | d2 = 11890; 528 | } 529 | if (tolower(s[0]) == 'b') { 530 | d1 = 10000; 531 | d2 = 14140; 532 | } 533 | if (tolower(s[0]) == 'c') { 534 | d1 = 9170; 535 | d2 = 12970; 536 | } 537 | n = s[1] - '0'; 538 | ps_pagewidth = ((n & 1) ? d2 : d1) >> ((n + 1) >> 1); 539 | ps_pageheight = ((n & 1) ? d1 : d2) >> (n >> 1); 540 | ps_pagewidth -= ps_pagewidth % 10; 541 | ps_pageheight -= ps_pageheight % 10; 542 | } 543 | 544 | void *mextend(void *old, long oldsz, long newsz, int memsz) 545 | { 546 | void *new = malloc(newsz * memsz); 547 | memcpy(new, old, oldsz * memsz); 548 | memset(new + oldsz * memsz, 0, (newsz - oldsz) * memsz); 549 | free(old); 550 | return new; 551 | } 552 | 553 | /* the unicode codepoint of the given utf-8 character */ 554 | static int utf8code(char *s) 555 | { 556 | int c = (unsigned char) s[0]; 557 | if (~c & 0xc0) /* ASCII or invalid */ 558 | return c; 559 | if (~c & 0x20) 560 | return ((c & 0x1f) << 6) | (s[1] & 0x3f); 561 | if (~c & 0x10) 562 | return ((c & 0x0f) << 12) | ((s[1] & 0x3f) << 6) | (s[2] & 0x3f); 563 | if (~c & 0x08) 564 | return ((c & 0x07) << 18) | ((s[1] & 0x3f) << 12) | ((s[2] & 0x3f) << 6) | (s[3] & 0x3f); 565 | return c; 566 | } 567 | 568 | static int pdftext_ascii(char *s) 569 | { 570 | for (; *s; s++) 571 | if (((unsigned char) *s) & 0x80 || *s == '(' || *s == ')') 572 | return 0; 573 | return 1; 574 | } 575 | 576 | /* encode s as pdf text string */ 577 | static char *pdftext(char *s) 578 | { 579 | struct sbuf *sb = sbuf_make(); 580 | if (pdftext_ascii(s)) { 581 | sbuf_chr(sb, '('); 582 | sbuf_str(sb, s); 583 | sbuf_chr(sb, ')'); 584 | return sbuf_done(sb); 585 | } 586 | /* read utf-8 and write utf-16 */ 587 | sbuf_str(sb, "= 0 && c <= 0xd7ff) || (c >= 0xe000 && c <= 0xffff)) { 591 | sbuf_printf(sb, "%02X%02X", c >> 8, c & 0xff); 592 | } 593 | if (c >= 0x010000 && c <= 0x10ffff) { 594 | int c1 = 0xd800 + ((c - 0x10000) >> 10); 595 | int c2 = 0xdc00 + ((c - 0x10000) & 0x3ff); 596 | sbuf_printf(sb, "%02X%02X", c1 >> 8, c1 & 0xff); 597 | sbuf_printf(sb, "%02X%02X", c2 >> 8, c2 & 0xff); 598 | } 599 | s += utf8len((unsigned char) *s); 600 | } 601 | sbuf_chr(sb, '>'); 602 | return sbuf_done(sb); 603 | } 604 | 605 | /* encode s as pdf text string; returns a static buffer */ 606 | char *pdftext_static(char *s) 607 | { 608 | static char buf[1024]; 609 | char *r = pdftext(s); 610 | snprintf(buf, sizeof(buf), "%s", r); 611 | free(r); 612 | return buf; 613 | } 614 | 615 | static void cmdset(char *arg) 616 | { 617 | char *eq = strchr(arg, '='); 618 | if (eq != NULL) { 619 | *eq = '\0'; 620 | outset(arg, eq + 1); 621 | } 622 | } 623 | 624 | static char *usage = 625 | "Usage: neatpost [options] output\n" 626 | "Options:\n" 627 | " -F dir \tset font directory (" TROFFFDIR ")\n" 628 | " -p size \tset paper size (letter); e.g., a4, 2100x2970\n" 629 | " -t title\tspecify document title\n" 630 | " -w lwid \tdrawing line thickness in thousandths of an em (40)\n" 631 | " -l \tlandscape mode\n" 632 | " -n \talways draw glyphs by name (ps glyphshow)\n" 633 | " -d x=v \tset device-specific variables\n"; 634 | 635 | int main(int argc, char *argv[]) 636 | { 637 | int i; 638 | int landscape = 0; 639 | if (getenv("NEATROFF_F") != NULL) 640 | snprintf(postdir, sizeof(postdir), "%s", getenv("NEATROFF_F")); 641 | for (i = 1; i < argc && argv[i][0] == '-'; i++) { 642 | switch (argv[i][1]) { 643 | case 'F': 644 | strcpy(postdir, argv[i][2] ? argv[i] + 2 : argv[++i]); 645 | break; 646 | case 'p': 647 | setpagesize(argv[i][2] ? argv[i] + 2 : argv[++i]); 648 | break; 649 | case 'w': 650 | ps_linewidth = atoi(argv[i][2] ? argv[i] + 2 : argv[++i]); 651 | break; 652 | case 'n': 653 | outgname(1); 654 | break; 655 | case 't': 656 | ps_title = argv[i][2] ? argv[i] + 2 : argv[++i]; 657 | break; 658 | case 'l': 659 | landscape = 1; 660 | break; 661 | case 'd': 662 | cmdset(argv[i][2] ? argv[i] + 2 : argv[++i]); 663 | break; 664 | default: 665 | fprintf(stderr, "%s", usage); 666 | return 1; 667 | } 668 | } 669 | if (landscape) { 670 | int t = ps_pagewidth; 671 | ps_pagewidth = ps_pageheight; 672 | ps_pageheight = t; 673 | } 674 | post(); 675 | doctrailer(o_pages); 676 | dev_close(); 677 | free(mark_desc); 678 | free(mark_page); 679 | free(mark_offset); 680 | free(mark_level); 681 | free(name_desc); 682 | free(name_page); 683 | free(name_offset); 684 | return 0; 685 | } 686 | -------------------------------------------------------------------------------- /post.h: -------------------------------------------------------------------------------- 1 | /* predefined array limits */ 2 | #define PATHLEN 1024 /* path length */ 3 | #define NFONTS 1024 /* number of fonts */ 4 | #define FNLEN 64 /* font name length */ 5 | #define GNLEN 32 /* glyph name length */ 6 | #define GNFMT "%31s" /* glyph name scanf format */ 7 | #define ILNLEN 1000 /* line limit of input files */ 8 | 9 | #define MIN(a, b) ((a) < (b) ? (a) : (b)) 10 | #define MAX(a, b) ((a) < (b) ? (b) : (a)) 11 | #define LEN(a) (sizeof(a) / sizeof((a)[0])) 12 | 13 | /* device related variables */ 14 | extern int dev_res; 15 | extern int dev_uwid; 16 | extern int dev_hor; 17 | extern int dev_ver; 18 | 19 | struct glyph { 20 | char id[GNLEN]; /* device-dependent glyph identifier */ 21 | char name[GNLEN]; /* the first character mapped to this glyph */ 22 | struct font *font; /* glyph font */ 23 | int wid; /* character width */ 24 | int type; /* character type; ascender/descender */ 25 | int pos; /* glyph code */ 26 | }; 27 | 28 | /* output device functions */ 29 | int dev_open(char *dir, char *dev); 30 | void dev_close(void); 31 | int dev_mnt(int pos, char *id, char *name); 32 | struct font *dev_font(int fn); 33 | int dev_fontid(struct font *fn); 34 | struct glyph *dev_glyph(char *c, int fn); 35 | struct font *dev_fontopen(char *name); 36 | 37 | /* font-related functions */ 38 | struct font *font_open(char *path); 39 | void font_close(struct font *fn); 40 | struct glyph *font_glyph(struct font *fn, char *id); 41 | struct glyph *font_find(struct font *fn, char *name); 42 | int font_wid(struct font *fn, int sz, int w); 43 | int font_swid(struct font *fn, int sz); 44 | char *font_name(struct font *fn); 45 | char *font_path(struct font *fn); 46 | int font_glnum(struct font *fn, struct glyph *g); 47 | struct glyph *font_glget(struct font *fn, int id); 48 | char *font_desc(struct font *fn); 49 | 50 | /* output functions */ 51 | void out(char *s, ...); 52 | void outc(char *s); 53 | void outh(int h); 54 | void outv(int v); 55 | void outrel(int h, int v); 56 | void outfont(int f); 57 | void outsize(int s); 58 | void outcolor(int c); 59 | void outrotate(int deg); 60 | void outeps(char *eps, int hwid, int vwid); 61 | void outpdf(char *pdf, int hwid, int vwid); 62 | void outlink(char *dst, int hwid, int vwid); 63 | void outmark(int n, char (*desc)[256], int *page, int *off, int *level); 64 | void outname(int n, char (*desc)[64], int *page, int *off); 65 | void outinfo(char *kwd, char *val); 66 | void outset(char *var, char *val); 67 | void outpage(void); 68 | void outmnt(int f); 69 | void outgname(int g); 70 | 71 | void drawbeg(void); 72 | void drawend(int close, int fill); 73 | void drawmbeg(char *s); 74 | void drawmend(char *s); 75 | void drawl(int h, int v); 76 | void drawc(int c); 77 | void drawe(int h, int v); 78 | void drawa(int h1, int v1, int h2, int v2); 79 | void draws(int h1, int v1, int h2, int v2); 80 | 81 | void docheader(char *title, int pagewidth, int pageheight, int linewidth); 82 | void doctrailer(int pages); 83 | void docpagebeg(int n); 84 | void docpageend(int n); 85 | 86 | /* colors */ 87 | #define CLR_R(c) (((c) >> 16) & 0xff) 88 | #define CLR_G(c) (((c) >> 8) & 0xff) 89 | #define CLR_B(c) ((c) & 0xff) 90 | #define CLR_RGB(r, g, b) (((r) << 16) | ((g) << 8) | (b)) 91 | 92 | char *clr_str(int c); 93 | int clr_get(char *s); 94 | 95 | /* mapping integers to sets */ 96 | struct iset *iset_make(void); 97 | void iset_free(struct iset *iset); 98 | int *iset_get(struct iset *iset, int key); 99 | void iset_put(struct iset *iset, int key, int ent); 100 | int iset_len(struct iset *iset, int key); 101 | 102 | /* mapping strings to longs */ 103 | struct dict *dict_make(int notfound, int dupkeys, int hashlen); 104 | void dict_free(struct dict *d); 105 | void dict_put(struct dict *d, char *key, int val); 106 | int dict_get(struct dict *d, char *key); 107 | int dict_idx(struct dict *d, char *key); 108 | char *dict_key(struct dict *d, int idx); 109 | int dict_val(struct dict *d, int idx); 110 | int dict_prefix(struct dict *d, char *key, int *idx); 111 | 112 | /* memory allocation */ 113 | void *mextend(void *old, long oldsz, long newsz, int memsz); 114 | /* helper functions */ 115 | char *pdftext_static(char *s); 116 | 117 | /* string buffers */ 118 | struct sbuf *sbuf_make(void); 119 | char *sbuf_buf(struct sbuf *sb); 120 | char *sbuf_done(struct sbuf *sb); 121 | void sbuf_free(struct sbuf *sb); 122 | int sbuf_len(struct sbuf *sbuf); 123 | void sbuf_str(struct sbuf *sbuf, char *s); 124 | void sbuf_printf(struct sbuf *sbuf, char *s, ...); 125 | void sbuf_chr(struct sbuf *sbuf, int c); 126 | void sbuf_mem(struct sbuf *sbuf, char *s, int len); 127 | void sbuf_cut(struct sbuf *sb, int len); 128 | 129 | /* reading PDF files */ 130 | int pdf_ws(char *pdf, int len, int pos); 131 | int pdf_len(char *pdf, int len, int pos); 132 | int pdf_type(char *pdf, int len, int pos); 133 | int pdf_dval(char *pdf, int len, int pos, char *key); 134 | int pdf_dkey(char *pdf, int len, int pos, int key); 135 | int pdf_lval(char *pdf, int len, int pos, int idx); 136 | int pdf_trailer(char *pdf, int len); 137 | int pdf_obj(char *pdf, int len, int pos, int *obj, int *rev); 138 | int pdf_find(char *pdf, int len, int obj, int rev); 139 | int pdf_ref(char *pdf, int len, int pos); 140 | int pdf_dval_val(char *pdf, int len, int pos, char *key); 141 | int pdf_dval_obj(char *pdf, int len, int pos, char *key); 142 | -------------------------------------------------------------------------------- /ps.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "post.h" 7 | 8 | static char ps_title[256]; /* document title */ 9 | static char ps_author[256]; /* document author */ 10 | static int ps_height; /* document height in basic units */ 11 | static int o_f, o_s, o_m; /* font and size */ 12 | static int o_h, o_v; /* current user position */ 13 | static int p_f, p_s, p_m; /* output postscript font */ 14 | static int o_qtype; /* queued character type */ 15 | static int o_qv, o_qh, o_qend; /* queued character position */ 16 | static int o_rh, o_rv, o_rdeg; /* previous rotation position and degree */ 17 | static int o_gname; /* use glyphshow for all glyphs */ 18 | 19 | static char o_fonts[FNLEN * NFONTS] = " "; 20 | 21 | static void outvf(char *s, va_list ap) 22 | { 23 | vfprintf(stdout, s, ap); 24 | } 25 | 26 | static void outf(char *s, ...) 27 | { 28 | va_list ap; 29 | va_start(ap, s); 30 | outvf(s, ap); 31 | va_end(ap); 32 | } 33 | 34 | static void o_flush(void) 35 | { 36 | if (o_qtype == 1) 37 | outf(") %d %d w\n", o_qh, o_qv); 38 | if (o_qtype == 2) 39 | outf("] %d %d g\n", o_qh, o_qv); 40 | o_qtype = 0; 41 | } 42 | 43 | void outgname(int g) 44 | { 45 | o_gname = g; 46 | } 47 | 48 | void outpage(void) 49 | { 50 | o_flush(); 51 | o_v = 0; 52 | o_h = 0; 53 | p_s = 0; 54 | p_f = 0; 55 | p_m = 0; 56 | o_rdeg = 0; 57 | } 58 | 59 | static void o_queue(struct glyph *g) 60 | { 61 | int type = 1 + (g->pos <= 0 || o_gname); 62 | if (o_qtype != type || o_qend != o_h || o_qv != o_v) { 63 | o_flush(); 64 | o_qh = o_h; 65 | o_qv = o_v; 66 | o_qtype = type; 67 | outf(type == 1 ? "(" : "["); 68 | } 69 | if (o_qtype == 1) { 70 | if (g->pos >= ' ' && g->pos <= '~') 71 | outf("%s%c", strchr("()\\", g->pos) ? "\\" : "", g->pos); 72 | else 73 | outf("\\%d%d%d", (g->pos >> 6) & 7, 74 | (g->pos >> 3) & 7, g->pos & 7); 75 | } else { 76 | outf("/%s", g->id); 77 | } 78 | o_qend = o_h + font_wid(g->font, o_s, g->wid); 79 | } 80 | 81 | /* calls o_flush() if necessary */ 82 | void out(char *s, ...) 83 | { 84 | va_list ap; 85 | o_flush(); 86 | va_start(ap, s); 87 | outvf(s, ap); 88 | va_end(ap); 89 | } 90 | 91 | static void out_fontup(int fid) 92 | { 93 | char fnname[FNLEN]; 94 | struct font *fn; 95 | if (o_m != p_m) { 96 | out("%d %d %d rgb\n", CLR_R(o_m), CLR_G(o_m), CLR_B(o_m)); 97 | p_m = o_m; 98 | } 99 | if (fid != p_f || o_s != p_s) { 100 | fn = dev_font(fid); 101 | out("%d /%s f\n", o_s, font_name(fn)); 102 | p_f = fid; 103 | p_s = o_s; 104 | sprintf(fnname, " %s ", font_name(fn)); 105 | if (!strstr(o_fonts, fnname)) 106 | sprintf(strchr(o_fonts, '\0'), "%s ", font_name(fn)); 107 | } 108 | } 109 | 110 | void outc(char *c) 111 | { 112 | struct glyph *g; 113 | struct font *fn; 114 | g = dev_glyph(c, o_f); 115 | fn = g ? g->font : dev_font(o_f); 116 | if (!g) { 117 | outrel(*c == ' ' && fn ? font_swid(fn, o_s) : 1, 0); 118 | return; 119 | } 120 | out_fontup(dev_fontid(fn)); 121 | o_queue(g); 122 | } 123 | 124 | void outh(int h) 125 | { 126 | o_h = h; 127 | } 128 | 129 | void outv(int v) 130 | { 131 | o_v = v; 132 | } 133 | 134 | void outrel(int h, int v) 135 | { 136 | o_h += h; 137 | o_v += v; 138 | } 139 | 140 | void outfont(int f) 141 | { 142 | if (dev_font(f)) 143 | o_f = f; 144 | } 145 | 146 | /* a font was mounted at pos f */ 147 | void outmnt(int f) 148 | { 149 | if (p_f == f) 150 | p_f = -1; 151 | } 152 | 153 | void outsize(int s) 154 | { 155 | if (s > 0) 156 | o_s = s; 157 | } 158 | 159 | void outcolor(int c) 160 | { 161 | o_m = c; 162 | } 163 | 164 | void outrotate(int deg) 165 | { 166 | o_flush(); 167 | out_fontup(o_f); 168 | if (o_rdeg) 169 | outf("%d %d %d rot\n", -o_rdeg, o_rh, o_rv); 170 | o_rdeg = deg; 171 | o_rh = o_h; 172 | o_rv = o_v; 173 | outf("%d %d %d rot\n", deg, o_h, o_v); 174 | } 175 | 176 | static int draw_path; /* number of path segments */ 177 | static int draw_point; /* point was set for postscript newpath */ 178 | 179 | static void drawmv(void) 180 | { 181 | if (!draw_point) 182 | outf("%d %d m ", o_h, o_v); 183 | draw_point = 1; 184 | } 185 | 186 | /* start a multi-segment path */ 187 | void drawmbeg(char *s) 188 | { 189 | o_flush(); 190 | out_fontup(o_f); 191 | draw_path = 1; 192 | outf("gsave newpath %s\n", s); 193 | } 194 | 195 | /* end a multi-segment path */ 196 | void drawmend(char *s) 197 | { 198 | draw_path = 0; 199 | draw_point = 0; 200 | outf("%s grestore\n", s); 201 | } 202 | 203 | void drawbeg(void) 204 | { 205 | o_flush(); 206 | out_fontup(o_f); 207 | if (draw_path) 208 | return; 209 | outf("newpath "); 210 | } 211 | 212 | void drawend(int close, int fill) 213 | { 214 | if (draw_path) 215 | return; 216 | draw_point = 0; 217 | if (close) 218 | outf("closepath "); 219 | if (fill) 220 | outf("fill\n"); 221 | else 222 | outf("stroke\n"); 223 | } 224 | 225 | void drawl(int h, int v) 226 | { 227 | drawmv(); 228 | outrel(h, v); 229 | outf("%d %d drawl ", o_h, o_v); 230 | } 231 | 232 | void drawc(int c) 233 | { 234 | drawmv(); 235 | outrel(c, 0); 236 | outf("%d %d drawe ", c, c); 237 | } 238 | 239 | void drawe(int h, int v) 240 | { 241 | drawmv(); 242 | outrel(h, 0); 243 | outf("%d %d drawe ", h, v); 244 | } 245 | 246 | void drawa(int h1, int v1, int h2, int v2) 247 | { 248 | drawmv(); 249 | outf("%d %d %d %d drawa ", h1, v1, h2, v2); 250 | outrel(h1 + h2, v1 + v2); 251 | } 252 | 253 | void draws(int h1, int v1, int h2, int v2) 254 | { 255 | drawmv(); 256 | outf("%d %d %d %d %d %d draws ", o_h, o_v, o_h + h1, o_v + v1, 257 | o_h + h1 + h2, o_v + v1 + v2); 258 | outrel(h1, v1); 259 | } 260 | 261 | void outeps(char *eps, int hwid, int vwid) 262 | { 263 | char buf[1 << 12]; 264 | int llx, lly, urx, ury; 265 | FILE *filp; 266 | int nbb, ver; 267 | if (!(filp = fopen(eps, "r"))) 268 | return; 269 | if (!fgets(buf, sizeof(buf), filp)) { 270 | fprintf(stderr, "warning: file %s is empty\n", eps); 271 | fclose(filp); 272 | return; 273 | } 274 | if (sscanf(buf, "%%!PS-Adobe-%d.%d EPSF-%d.%d", &ver, &ver, &ver, &ver) != 4) { 275 | fprintf(stderr, "warning: unsupported EPSF header in %s\n", eps); 276 | fclose(filp); 277 | return; 278 | } 279 | nbb = 0; 280 | while (fgets(buf, sizeof(buf), filp)) 281 | if (!strncmp(buf, "%%BoundingBox: ", 15)) 282 | if ((nbb = sscanf(buf + 15, "%d %d %d %d", 283 | &llx, &lly, &urx, &ury)) == 4) 284 | break; 285 | fclose(filp); 286 | if (nbb < 4) /* no BoundingBox comment */ 287 | return; 288 | if (hwid <= 0 && vwid <= 0) 289 | hwid = (urx - llx) * dev_res / 72; 290 | if (vwid <= 0) 291 | vwid = (ury - lly) * hwid / (urx - llx); 292 | if (hwid <= 0) 293 | hwid = (urx - llx) * vwid / (ury - lly); 294 | /* output the EPS file */ 295 | o_flush(); 296 | out_fontup(o_f); 297 | outf("%d %d %d %d %d %d %d %d EPSFBEG\n", 298 | llx, lly, hwid, urx - llx, vwid, ury - lly, o_h, o_v); 299 | outf("%%%%BeginDocument: %s\n", eps); 300 | filp = fopen(eps, "r"); 301 | while (fgets(buf, sizeof(buf), filp)) 302 | out("%s", buf); 303 | fclose(filp); 304 | outf("%%%%EndDocument\n"); 305 | outf("EPSFEND\n"); 306 | } 307 | 308 | void outpdf(char *pdf, int hwid, int vwid) 309 | { 310 | } 311 | 312 | void outlink(char *lnk, int hwid, int vwid) 313 | { 314 | o_flush(); 315 | if (lnk[0] == '#' || isdigit((unsigned char) lnk[0])) { 316 | outf("[ /Rect [ %d %d t %d %d t ] %s%s " 317 | "/Subtype /Link /LNK pdfmark\n", 318 | o_h, o_v, o_h + hwid, o_v + vwid, 319 | lnk[0] == '#' ? "/Dest /" : "/Page ", 320 | lnk[0] == '#' ? lnk + 1 : lnk); 321 | } else { 322 | outf("[ /Rect [ %d %d t %d %d t ] " 323 | "/Action << /Subtype /URI /URI %s >> /Open true " 324 | "/Subtype /Link /LNK pdfmark\n", 325 | o_h, o_v, o_h + hwid, o_v + vwid, pdftext_static(lnk)); 326 | } 327 | } 328 | 329 | void outname(int n, char (*desc)[64], int *page, int *off) 330 | { 331 | int i; 332 | o_flush(); 333 | for (i = 0; i < n; i++) { 334 | outf("[ /Dest /%s", desc[i]); 335 | outf(" /Page %d", page[i]); 336 | if (off[i] > 0) 337 | outf(" /View [/XYZ null %d null]", 338 | (ps_height - off[i]) * 72 / dev_res); 339 | outf(" /DEST pdfmark\n"); 340 | } 341 | } 342 | 343 | void outmark(int n, char (*desc)[256], int *page, int *off, int *level) 344 | { 345 | int i, j; 346 | o_flush(); 347 | for (i = 0; i < n; i++) { 348 | int cnt = 0; 349 | for (j = i + 1; j < n && level[j] > level[i]; j++) 350 | if (level[j] == level[i] + 1) 351 | cnt++; 352 | outf("[ /Title %s", pdftext_static(desc[i])); 353 | if (page[i] > 0) 354 | outf(" /Page %d", page[i]); 355 | if (cnt > 0) 356 | outf(" /Count %d", cnt); 357 | if (off[i] > 0) 358 | outf(" /View [/XYZ null %d null]", 359 | (ps_height - off[i]) * 72 / dev_res); 360 | outf(" /OUT pdfmark\n"); 361 | } 362 | } 363 | 364 | void outinfo(char *kwd, char *val) 365 | { 366 | if (!strcmp("Author", kwd)) 367 | snprintf(ps_author, sizeof(ps_author), "%s", val); 368 | if (!strcmp("Title", kwd)) 369 | snprintf(ps_title, sizeof(ps_title), "%s", val); 370 | } 371 | 372 | void outset(char *var, char *val) 373 | { 374 | } 375 | 376 | void docpagebeg(int n) 377 | { 378 | out("%%%%Page: %d %d\n", n, n); 379 | out("/saveobj save def\n"); 380 | out("mark\n"); 381 | out("%d pagesetup\n", n); 382 | } 383 | 384 | void docpageend(int n) 385 | { 386 | out("cleartomark\n"); 387 | out("showpage\n"); 388 | out("saveobj restore\n"); 389 | } 390 | 391 | void doctrailer(int pages) 392 | { 393 | out("["); 394 | if (ps_title[0]) 395 | out(" /Title %s", pdftext_static(ps_title)); 396 | if (ps_author[0]) 397 | out(" /Author %s", pdftext_static(ps_author)); 398 | out(" /Creator (Neatroff) /DOCINFO pdfmark\n"); 399 | out("%%%%Trailer\n"); 400 | out("done\n"); 401 | out("%%%%DocumentFonts: %s\n", o_fonts); 402 | out("%%%%Pages: %d\n", pages); 403 | out("%%%%EOF\n"); 404 | } 405 | 406 | static char *prolog = 407 | "/setup {\n" 408 | " counttomark 2 idiv {def} repeat pop\n" 409 | " /scaling 72 resolution div def\n" 410 | " linewidth setlinewidth\n" 411 | " 1 setlinecap\n" 412 | " 0 pagesize 1 get translate\n" 413 | " scaling scaling scale\n" 414 | " 0 0 moveto\n" 415 | "} def\n" 416 | "\n" 417 | "/pagesetup {\n" 418 | " /page exch def\n" 419 | " currentdict /pagedict known currentdict page known and {\n" 420 | " page load pagedict exch get cvx exec\n" 421 | " } if\n" 422 | "} def\n" 423 | "\n" 424 | "/pdfmark where\n" 425 | " { pop globaldict /?pdfmark /exec load put }\n" 426 | " { globaldict begin\n" 427 | " /?pdfmark /pop load def\n" 428 | " /pdfmark /cleartomark load def\n" 429 | " end }\n" 430 | " ifelse\n" 431 | "\n" 432 | "/t {neg} bind def\n" 433 | "/w {neg moveto show} bind def\n" 434 | "/m {neg moveto} bind def\n" 435 | "/g {neg moveto {glyphshow} forall} bind def\n" 436 | "/rgb {255 div 3 1 roll 255 div 3 1 roll 255 div 3 1 roll setrgbcolor} bind def\n" 437 | "/rot {/y exch def /x exch def x y neg translate rotate x neg y translate} bind def\n" 438 | "/done {/lastpage where {pop lastpage} if} def\n" 439 | "\n" 440 | "% caching fonts, as selectfont is supposed to be doing\n" 441 | "/fncache 16 dict def\n" 442 | "/selectfont_append { fncache exch dup findfont put } bind def\n" 443 | "/selectfont_cached {\n" 444 | " exch dup fncache exch known not { dup selectfont_append } if\n" 445 | " fncache exch get exch scalefont setfont\n" 446 | "} bind def\n" 447 | "/f {\n" 448 | " exch dup 3 1 roll scaling div selectfont_cached\n" 449 | " linewidth mul scaling 10 mul div setlinewidth\n" 450 | "} bind def\n" 451 | "\n" 452 | "/savedmatrix matrix def\n" 453 | "/drawl {\n" 454 | " neg lineto\n" 455 | "} bind def\n" 456 | "/drawe {\n" 457 | " savedmatrix currentmatrix pop scale\n" 458 | " .5 0 rmoveto currentpoint .5 0 rmoveto .5 0 360 arc\n" 459 | " savedmatrix setmatrix\n" 460 | "} bind def\n" 461 | "/drawa {\n" 462 | " /dy2 exch def\n" 463 | " /dx2 exch def\n" 464 | " /dy1 exch def\n" 465 | " /dx1 exch def\n" 466 | " currentpoint dy1 neg add exch dx1 add exch\n" 467 | " dx1 dx1 mul dy1 dy1 mul add sqrt\n" 468 | " dy1 dx1 neg atan\n" 469 | " dy2 neg dx2 atan\n" 470 | " arc\n" 471 | "} bind def\n" 472 | "/draws {\n" 473 | " /y2 exch def\n" 474 | " /x2 exch def\n" 475 | " /y1 exch def\n" 476 | " /x1 exch def\n" 477 | " /y0 exch def\n" 478 | " /x0 exch def\n" 479 | " x0 5 x1 mul add 6 div\n" 480 | " y0 5 y1 mul add -6 div\n" 481 | " x2 5 x1 mul add 6 div\n" 482 | " y2 5 y1 mul add -6 div\n" 483 | " x1 x2 add 2 div\n" 484 | " y1 y2 add -2 div\n" 485 | " curveto\n" 486 | "} bind def\n" 487 | "% including EPS files\n" 488 | "/EPSFBEG {\n" 489 | " /epsf_state save def\n" 490 | " neg translate\n" 491 | " div 3 1 roll div exch scale\n" 492 | " neg exch neg exch translate\n" 493 | " /dict_count countdictstack def\n" 494 | " /op_count count 1 sub def\n" 495 | " userdict begin\n" 496 | " /showpage { } def\n" 497 | " 0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n" 498 | " 10 setmiterlimit [ ] 0 setdash newpath\n" 499 | "} bind def\n" 500 | "/EPSFEND {\n" 501 | " count op_count sub {pop} repeat\n" 502 | " countdictstack dict_count sub {end} repeat\n" 503 | " epsf_state restore\n" 504 | "} bind def\n"; 505 | 506 | /* pagewidth and pageheight are in tenths of a millimetre */ 507 | void docheader(char *title, int pagewidth, int pageheight, int linewidth) 508 | { 509 | ps_height = pageheight * dev_res / 254; 510 | out("%%!PS-Adobe-2.0\n"); 511 | out("%%%%Version: 1.0\n"); 512 | if (title) 513 | out("%%%%Title: %s\n", title); 514 | out("%%%%Creator: Neatroff\n"); 515 | out("%%%%DocumentFonts: (atend)\n"); 516 | out("%%%%Pages: (atend)\n"); 517 | out("%%%%EndComments\n"); 518 | 519 | out("%%%%BeginProlog\n"); 520 | out("/resolution %d def\n", dev_res); 521 | out("/pagesize [%d %d] def\n", (pagewidth * 72 + 127) / 254, 522 | (pageheight * 72 + 127) / 254); 523 | out("/linewidth %d.%02d def\n\n", linewidth / 100, linewidth % 100); 524 | out("%s", prolog); 525 | out("%%%%EndProlog\n"); 526 | out("%%%%BeginSetup\n"); 527 | out("<< /PageSize pagesize /ImagingBBox null >> setpagedevice\n"); 528 | out("mark\n"); 529 | out("setup\n"); 530 | out("%%%%EndSetup\n"); 531 | } 532 | -------------------------------------------------------------------------------- /sbuf.c: -------------------------------------------------------------------------------- 1 | /* variable length string buffer */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "post.h" 7 | 8 | #define SBUFSZ 128 9 | #define ALIGN(n, a) (((n) + (a) - 1) & ~((a) - 1)) 10 | #define NEXTSZ(o, r) ALIGN(MAX((o) * 2, (o) + (r)), SBUFSZ) 11 | 12 | struct sbuf { 13 | char *s; /* allocated buffer */ 14 | int s_n; /* length of the string stored in s[] */ 15 | int s_sz; /* size of memory allocated for s[] */ 16 | }; 17 | 18 | static void sbuf_extend(struct sbuf *sbuf, int newsz) 19 | { 20 | char *s = sbuf->s; 21 | sbuf->s_sz = newsz; 22 | sbuf->s = malloc(sbuf->s_sz); 23 | if (sbuf->s_n) 24 | memcpy(sbuf->s, s, sbuf->s_n); 25 | free(s); 26 | } 27 | 28 | struct sbuf *sbuf_make(void) 29 | { 30 | struct sbuf *sb = malloc(sizeof(*sb)); 31 | memset(sb, 0, sizeof(*sb)); 32 | return sb; 33 | } 34 | 35 | char *sbuf_buf(struct sbuf *sb) 36 | { 37 | if (!sb->s) 38 | sbuf_extend(sb, 1); 39 | sb->s[sb->s_n] = '\0'; 40 | return sb->s; 41 | } 42 | 43 | char *sbuf_done(struct sbuf *sb) 44 | { 45 | char *s = sbuf_buf(sb); 46 | free(sb); 47 | return s; 48 | } 49 | 50 | void sbuf_free(struct sbuf *sb) 51 | { 52 | free(sb->s); 53 | free(sb); 54 | } 55 | 56 | void sbuf_chr(struct sbuf *sbuf, int c) 57 | { 58 | if (sbuf->s_n + 2 >= sbuf->s_sz) 59 | sbuf_extend(sbuf, NEXTSZ(sbuf->s_sz, 1)); 60 | sbuf->s[sbuf->s_n++] = c; 61 | } 62 | 63 | void sbuf_mem(struct sbuf *sbuf, char *s, int len) 64 | { 65 | if (sbuf->s_n + len + 1 >= sbuf->s_sz) 66 | sbuf_extend(sbuf, NEXTSZ(sbuf->s_sz, len + 1)); 67 | memcpy(sbuf->s + sbuf->s_n, s, len); 68 | sbuf->s_n += len; 69 | } 70 | 71 | void sbuf_str(struct sbuf *sbuf, char *s) 72 | { 73 | sbuf_mem(sbuf, s, strlen(s)); 74 | } 75 | 76 | int sbuf_len(struct sbuf *sbuf) 77 | { 78 | return sbuf->s_n; 79 | } 80 | 81 | void sbuf_cut(struct sbuf *sb, int len) 82 | { 83 | if (sb->s_n > len) 84 | sb->s_n = len; 85 | } 86 | 87 | void sbuf_printf(struct sbuf *sbuf, char *s, ...) 88 | { 89 | char buf[256]; 90 | va_list ap; 91 | va_start(ap, s); 92 | vsnprintf(buf, sizeof(buf), s, ap); 93 | va_end(ap); 94 | sbuf_str(sbuf, buf); 95 | } 96 | -------------------------------------------------------------------------------- /txt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "post.h" 7 | 8 | static int o_f, o_s, o_m; /* font and size */ 9 | static int o_h, o_v; /* current user position */ 10 | static int p_wd, p_ht; /* page width and height in basic units */ 11 | static int c_wdpt = 10, c_htpt = 12; /* glyph cell width and height in points */ 12 | static int c_wd, c_ht; /* glyph cell width and height in basic units */ 13 | static int p_cwd, p_cht; /* page dimentions in cells */ 14 | static char *o_pg; /* output page buffer */ 15 | 16 | void outgname(int g) 17 | { 18 | } 19 | 20 | void outpage(void) 21 | { 22 | o_v = 0; 23 | o_h = 0; 24 | } 25 | 26 | /* calls o_flush() if necessary */ 27 | void out(char *s, ...) 28 | { 29 | va_list ap; 30 | va_start(ap, s); 31 | vfprintf(stdout, s, ap); 32 | va_end(ap); 33 | } 34 | 35 | void outc(char *c) 36 | { 37 | struct glyph *g = dev_glyph(c, o_f); 38 | int v = o_v / c_ht - 1; 39 | int h = o_h / c_wd; 40 | if (h >= 0 && v >= 0 && h < p_cwd && v < p_cht) 41 | o_pg[v * p_cwd + h] = g->name[0]; 42 | } 43 | 44 | void outh(int h) 45 | { 46 | o_h = h; 47 | } 48 | 49 | void outv(int v) 50 | { 51 | o_v = v; 52 | } 53 | 54 | void outrel(int h, int v) 55 | { 56 | o_h += h; 57 | o_v += v; 58 | } 59 | 60 | void outfont(int f) 61 | { 62 | if (dev_font(f)) 63 | o_f = f; 64 | } 65 | 66 | void outmnt(int f) 67 | { 68 | } 69 | 70 | void outsize(int s) 71 | { 72 | if (s > 0) 73 | o_s = s; 74 | } 75 | 76 | void outcolor(int c) 77 | { 78 | o_m = c; 79 | } 80 | 81 | void outrotate(int deg) 82 | { 83 | } 84 | 85 | void drawmbeg(char *s) 86 | { 87 | } 88 | 89 | void drawmend(char *s) 90 | { 91 | } 92 | 93 | void drawbeg(void) 94 | { 95 | } 96 | 97 | void drawend(int close, int fill) 98 | { 99 | } 100 | 101 | void drawl(int h, int v) 102 | { 103 | outrel(h, v); 104 | } 105 | 106 | void drawc(int c) 107 | { 108 | outrel(c, 0); 109 | } 110 | 111 | void drawe(int h, int v) 112 | { 113 | outrel(h, 0); 114 | } 115 | 116 | void drawa(int h1, int v1, int h2, int v2) 117 | { 118 | outrel(h1 + h2, v1 + v2); 119 | } 120 | 121 | void draws(int h1, int v1, int h2, int v2) 122 | { 123 | outrel(h1, v1); 124 | } 125 | 126 | void outeps(char *eps, int hwid, int vwid) 127 | { 128 | } 129 | 130 | void outpdf(char *pdf, int hwid, int vwid) 131 | { 132 | } 133 | 134 | void outlink(char *lnk, int hwid, int vwid) 135 | { 136 | } 137 | 138 | void outname(int n, char (*desc)[64], int *page, int *off) 139 | { 140 | } 141 | 142 | void outmark(int n, char (*desc)[256], int *page, int *off, int *level) 143 | { 144 | } 145 | 146 | void outinfo(char *kwd, char *val) 147 | { 148 | } 149 | 150 | void outset(char *var, char *val) 151 | { 152 | if (!strcmp(var, "cellht")) 153 | c_htpt = atoi(val); 154 | if (!strcmp(var, "cellwd")) 155 | c_wdpt = atoi(val); 156 | } 157 | 158 | void docpagebeg(int n) 159 | { 160 | memset(o_pg, 0, p_cwd * p_cht); 161 | if (n > 1) 162 | out(" \n"); 163 | } 164 | 165 | void docpageend(int n) 166 | { 167 | int lastln = 0; 168 | int i, j; 169 | for (i = 0; i < p_cht; i++) 170 | for (j = 0; j < p_cwd; j++) 171 | if (o_pg[i * p_cwd + j]) 172 | lastln = i + 1; 173 | for (i = 0; i < lastln; i++) { 174 | int lastcol = 0; 175 | for (j = 0; j < p_cwd; j++) 176 | if (o_pg[i * p_cwd + j]) 177 | lastcol = j + 1; 178 | for (j = 0; j < lastcol; j++) { 179 | int c = o_pg[i * p_cwd + j]; 180 | printf("%c", c ? c : ' '); 181 | } 182 | printf("\n"); 183 | } 184 | } 185 | 186 | void doctrailer(int pages) 187 | { 188 | free(o_pg); 189 | } 190 | 191 | void docheader(char *title, int pagewidth, int pageheight, int linewidth) 192 | { 193 | p_wd = pagewidth * dev_res / 254; 194 | p_ht = pageheight * dev_res / 254; 195 | c_wd = dev_res * c_wdpt / 72; 196 | c_ht = dev_res * c_htpt / 72; 197 | p_cwd = p_wd / c_wd; 198 | p_cht = p_ht / c_ht; 199 | o_pg = malloc(p_cwd * p_cht); 200 | } 201 | --------------------------------------------------------------------------------