1 /* draw.c */
2
3 /************************************************************************
4
5 Part of the dvipng distribution
6
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation, either version 3 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this program. If not, see
19 <http://www.gnu.org/licenses/>.
20
21 Copyright (C) 2002-2015 Jan-�ke Larsson
22
23 ************************************************************************/
24
25 #include "dvipng.h"
26
27 #ifdef DEBUG
28 #include <ctype.h> /* isprint */
29 #endif
30
31 struct stack_entry {
32 dviunits h, v, w, x, y, z; /* stack entry */
33 subpixels hh,vv;
34 } stack[STACK_SIZE+1]; /* stack + space for current pos */
35 struct stack_entry* dvi_stack=stack;
36
37 #define MAXDRIFT 1
38 #define CHECK_MAXDRIFT(x,xx) \
39 if ( xx-PIXROUND(x,dvi->conv*shrinkfactor) < -MAXDRIFT ) { \
40 DEBUG_PRINT(DEBUG_DVI,(" add 1 to")); \
41 xx += 1; \
42 } \
43 if ( xx-PIXROUND(x,dvi->conv*shrinkfactor) > MAXDRIFT ) { \
44 DEBUG_PRINT(DEBUG_DVI,(" sub 1 to")); \
45 xx -= 1; \
46 } \
47 if (PIXROUND(dvi_stack->h,dvi->conv*shrinkfactor) != dvi_stack->hh \
48 || PIXROUND(dvi_stack->v,dvi->conv*shrinkfactor) != dvi_stack->vv)\
49 DEBUG_PRINT(DEBUG_DVI, \
50 (" drift (%d,%d)", \
51 dvi_stack->hh-PIXROUND(dvi_stack->h,dvi->conv*shrinkfactor), \
52 dvi_stack->vv-PIXROUND(dvi_stack->v,dvi->conv*shrinkfactor)));
53
54 #define MoveRight(x) \
55 temp=x; dvi_stack->h += temp; \
56 if ( currentfont==NULL \
57 || temp > currentfont->s/6 || temp < -currentfont->s/6*4 ) \
58 dvi_stack->hh = PIXROUND(dvi_stack->h,dvi->conv*shrinkfactor); \
59 else \
60 dvi_stack->hh += PIXROUND( temp,dvi->conv*shrinkfactor ); \
61 CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh)
62
63 #define MoveDown(x) \
64 temp=x; dvi_stack->v += temp; \
65 if ( currentfont==NULL \
66 || temp > currentfont->s/6*5 || temp < currentfont->s/6*(-5) ) \
67 dvi_stack->vv = PIXROUND(dvi_stack->v,dvi->conv*shrinkfactor); \
68 else \
69 dvi_stack->vv += PIXROUND( temp,dvi->conv*shrinkfactor ); \
70 CHECK_MAXDRIFT(dvi_stack->v,dvi_stack->vv)
71
72 #define DO_VFCONV(a) ((((struct font_entry*) parent)->type==DVI_TYPE)?a:\
73 (dviunits)((int64_t) a * ((struct font_entry*) parent)->s / (1 << 20)))
74
75
SetChar(int32_t c)76 dviunits SetChar(int32_t c)
77 {
78 struct char_entry* ptr=NULL;
79
80 if (currentfont==NULL)
81 Fatal("faulty DVI, trying to set character from null font");
82 if (c<0 || c>LASTFNTCHAR) {
83 Warning("glyph index out of range (%d), skipping",c);
84 return(0);
85 }
86 ptr=currentfont->chr[c];
87 if (ptr==NULL) {
88 Warning("unable to draw glyph %d, skipping",c);
89 return(0);
90 }
91 #ifdef DEBUG
92 switch (currentfont->type) {
93 case FONT_TYPE_VF: DEBUG_PRINT(DEBUG_DVI,("\n VF CHAR:\t")); break;
94 case FONT_TYPE_PK: DEBUG_PRINT(DEBUG_DVI,("\n PK CHAR:\t")); break;
95 case FONT_TYPE_FT: DEBUG_PRINT(DEBUG_DVI,("\n FT CHAR:\t")); break;
96 default: DEBUG_PRINT(DEBUG_DVI,("\n NO CHAR:\t"))
97 }
98 if (debug & DEBUG_DVI && c>=0 && c<=UCHAR_MAX && isprint(c))
99 DEBUG_PRINT(DEBUG_DVI,("'%c' ",c));
100 DEBUG_PRINT(DEBUG_DVI,("%d at (%d,%d) tfmw %d", c,
101 dvi_stack->hh,dvi_stack->vv,ptr?ptr->tfmw:0));
102 #endif
103 if (currentfont->type==FONT_TYPE_VF) {
104 return(SetVF(ptr));
105 } else {
106 if (ptr->data == NULL)
107 switch(currentfont->type) {
108 case FONT_TYPE_PK: LoadPK(c, ptr); break;
109 #ifdef HAVE_FT2
110 case FONT_TYPE_FT: LoadFT(c, ptr); break;
111 #endif
112 default:
113 Fatal("undefined fonttype %d",currentfont->type);
114 }
115 if (page_imagep != NULL)
116 return(SetGlyph(ptr, dvi_stack->hh, dvi_stack->vv));
117 else {
118 /* Expand bounding box if necessary */
119 min(x_min,dvi_stack->hh - ptr->xOffset/shrinkfactor);
120 min(y_min,dvi_stack->vv - ptr->yOffset/shrinkfactor);
121 max(x_max,dvi_stack->hh - ptr->xOffset/shrinkfactor + ptr->w);
122 max(y_max,dvi_stack->vv - ptr->yOffset/shrinkfactor + ptr->h);
123 return(ptr->tfmw);
124 }
125 }
126 return(0);
127 }
128
DrawCommand(unsigned char * command,void * parent)129 void DrawCommand(unsigned char* command, void* parent /* dvi/vf */)
130 /* To be used both in plain DVI drawing and VF drawing */
131 {
132 dviunits temp;
133
134 if (/*command >= SETC_000 &&*/ *command <= SETC_127) {
135 temp = SetChar((int32_t)*command);
136 dvi_stack->h += temp;
137 dvi_stack->hh += PIXROUND(temp,dvi->conv*shrinkfactor);
138 CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh);
139 } else if (*command >= FONT_00 && *command <= FONT_63) {
140 SetFntNum((int32_t)*command - FONT_00,parent);
141 } else switch (*command) {
142 case PUT1: case PUT2: case PUT3: case PUT4:
143 DEBUG_PRINT(DEBUG_DVI,(" %d",
144 UNumRead(command+1, dvi_commandlength[*command]-1)));
145 (void) SetChar(UNumRead(command+1, dvi_commandlength[*command]-1));
146 break;
147 case SET1: case SET2: case SET3: case SET4:
148 DEBUG_PRINT(DEBUG_DVI,(" %d",
149 UNumRead(command+1, dvi_commandlength[*command]-1)));
150 {
151 temp = SetChar(UNumRead(command+1, dvi_commandlength[*command]-1));
152 dvi_stack->h += temp;
153 dvi_stack->hh += PIXROUND(temp,dvi->conv*shrinkfactor);
154 CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh);
155 }
156 break;
157 case SET_RULE:
158 DEBUG_PRINT(DEBUG_DVI,(" %d %d",
159 UNumRead(command+1, 4), UNumRead(command+5, 4)));
160 temp = SetRule(DO_VFCONV(UNumRead(command+1, 4)),
161 DO_VFCONV(UNumRead(command+5, 4)),
162 dvi_stack->hh, dvi_stack->vv);
163 dvi_stack->h += temp;
164 dvi_stack->hh += PIXROUND(temp,dvi->conv*shrinkfactor);
165 CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh);
166 break;
167 case PUT_RULE:
168 DEBUG_PRINT(DEBUG_DVI,(" %d %d",
169 UNumRead(command+1, 4), UNumRead(command+5, 4)));
170 (void) SetRule(DO_VFCONV(UNumRead(command+1, 4)),
171 DO_VFCONV(UNumRead(command+5, 4)),
172 dvi_stack->hh, dvi_stack->vv);
173 break;
174 case BOP:
175 Fatal("BOP occurs within page");
176 break;
177 case EOP:
178 break;
179 case PUSH:
180 /* is next item on stack? */
181 if (dvi_stack == &stack[STACK_SIZE-1])
182 Fatal("DVI stack overflow");
183 {
184 struct stack_entry *next=dvi_stack+1;
185 next->h = dvi_stack->h;
186 next->v = dvi_stack->v;
187 next->w = dvi_stack->w;
188 next->x = dvi_stack->x;
189 next->y = dvi_stack->y;
190 next->z = dvi_stack->z;
191 next->hh = dvi_stack->hh;
192 next->vv = dvi_stack->vv;
193 dvi_stack=next;
194 }
195 break;
196 case POP:
197 if (dvi_stack == stack)
198 Fatal("DVI stack underflow");
199 dvi_stack--;
200 break;
201 case RIGHT1: case RIGHT2: case RIGHT3: case RIGHT4:
202 DEBUG_PRINT(DEBUG_DVI,(" %d",
203 SNumRead(command+1, dvi_commandlength[*command]-1)));
204 MoveRight(DO_VFCONV(SNumRead(command+1, dvi_commandlength[*command]-1)));
205 break;
206 case W1: case W2: case W3: case W4:
207 dvi_stack->w = SNumRead(command+1, dvi_commandlength[*command]-1);
208 DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->w));
209 case W0:
210 MoveRight(DO_VFCONV(dvi_stack->w));
211 break;
212 case X1: case X2: case X3: case X4:
213 dvi_stack->x = SNumRead(command+1, dvi_commandlength[*command]-1);
214 DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->x));
215 case X0:
216 MoveRight(DO_VFCONV(dvi_stack->x));
217 break;
218 case DOWN1: case DOWN2: case DOWN3: case DOWN4:
219 DEBUG_PRINT(DEBUG_DVI,(" %d",
220 SNumRead(command+1, dvi_commandlength[*command]-1)));
221 MoveDown(DO_VFCONV(SNumRead(command+1, dvi_commandlength[*command]-1)));
222 break;
223 case Y1: case Y2: case Y3: case Y4:
224 dvi_stack->y = SNumRead(command+1, dvi_commandlength[*command]-1);
225 DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->y));
226 case Y0:
227 MoveDown(DO_VFCONV(dvi_stack->y));
228 break;
229 case Z1: case Z2: case Z3: case Z4:
230 dvi_stack->z = SNumRead(command+1, dvi_commandlength[*command]-1);
231 DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->z));
232 case Z0:
233 MoveDown(DO_VFCONV(dvi_stack->z));
234 break;
235 case FNT1: case FNT2: case FNT3: case FNT4:
236 DEBUG_PRINT(DEBUG_DVI,(" %d",
237 UNumRead(command+1, dvi_commandlength[*command]-1)));
238 SetFntNum(UNumRead(command+1, dvi_commandlength[*command]-1),parent);
239 break;
240 case XXX1: case XXX2: case XXX3: case XXX4:
241 DEBUG_PRINT(DEBUG_DVI,(" %d",
242 UNumRead(command+1, dvi_commandlength[*command]-1)));
243 SetSpecial((char*)command + dvi_commandlength[*command],
244 (char*)command + dvi_commandlength[*command]
245 +UNumRead(command+1, dvi_commandlength[*command]-1),
246 dvi_stack->hh,dvi_stack->vv);
247 break;
248 case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4:
249 if (((struct font_entry*)parent)->type==DVI_TYPE) {
250 FontDef(command, parent);
251 } else {
252 Fatal("%s within VF macro from %s",dvi_commands[*command],
253 ((struct font_entry*)parent)->n);
254 }
255 break;
256 case PRE: case POST: case POST_POST:
257 Fatal("%s occurs within page",dvi_commands[*command]);
258 break;
259 case NOP:
260 break;
261 default:
262 Fatal("%s is an undefined command",dvi_commands[*command]);
263 break;
264 }
265 }
266
BeginVFMacro(struct font_entry * currentvf)267 void BeginVFMacro(struct font_entry* currentvf)
268 {
269 struct stack_entry *next=dvi_stack+1;
270 if (dvi_stack == &stack[STACK_SIZE-1])
271 Fatal("DVI stack overflow");
272 next->h = dvi_stack->h;
273 next->v = dvi_stack->v;
274 next->w = next->x = next->y = next->z = 0;
275 next->hh = dvi_stack->hh;
276 next->vv = dvi_stack->vv;
277 dvi_stack = next;
278 DEBUG_PRINT(DEBUG_DVI,("\n START VF:\tPUSH, W = X = Y = Z = 0"));
279 SetFntNum(currentvf->defaultfont,currentvf);
280 }
281
EndVFMacro(void)282 void EndVFMacro(void)
283 {
284 if (dvi_stack == stack)
285 Fatal("DVI stack underflow");
286 dvi_stack--;
287 DEBUG_PRINT(DEBUG_DVI,("\n END VF:\tPOP "));
288 }
289
290
DrawPage(dviunits hoffset,dviunits voffset)291 static void DrawPage(dviunits hoffset, dviunits voffset)
292 /* To be used after having read BOP and will exit cleanly when
293 * encountering EOP.
294 */
295 {
296 unsigned char* command; /* current command */
297
298 dvi_stack->h = hoffset;
299 dvi_stack->v = voffset;
300 dvi_stack->w = dvi_stack->x = dvi_stack->y = dvi_stack->z = 0;
301 dvi_stack->hh = PIXROUND( dvi_stack->h , dvi->conv*shrinkfactor );
302 dvi_stack->vv = PIXROUND( dvi_stack->v , dvi->conv*shrinkfactor );
303 currentfont = NULL; /* No default font */
304
305 command=DVIGetCommand(dvi);
306 DEBUG_PRINT(DEBUG_DVI,("DRAW CMD:\t%s", dvi_commands[*command]));
307 while (*command != EOP) {
308 DrawCommand(command,dvi);
309 command=DVIGetCommand(dvi);
310 DEBUG_PRINT(DEBUG_DVI,("DRAW CMD:\t%s", dvi_commands[*command]));
311 }
312 }
313
DrawPages(void)314 void DrawPages(void)
315 {
316 struct page_list *dvi_pos;
317 pixels x_width,y_width,x_offset,y_offset;
318 int pagecounter=(option_flags & DVI_PAGENUM)?0:10;
319
320 dvi_pos=NextPPage(dvi,NULL);
321 if (dvi_pos!=NULL) {
322 while(dvi_pos!=NULL) {
323 SeekPage(dvi,dvi_pos);
324 Message(BE_NONQUIET,"[%d", dvi_pos->count[pagecounter]);
325 if (dvi_pos->count[pagecounter]!=dvi_pos->count[0])
326 Message(BE_NONQUIET," (%d)", dvi_pos->count[0]);
327 x_max = y_max = INT32_MIN;
328 x_min = y_min = INT32_MAX;
329 DrawPage((dviunits)0,(dviunits)0);
330 /* Store background color. Background color of a page is given
331 by the color at EOP rather than the color at BOP. */
332 StoreBackgroundColor(dvi_pos);
333 /* Store pagesize */
334 if (dvi->flags & DVI_PREVIEW_LATEX_TIGHTPAGE) {
335 x_width_def=x_width_tightpage;
336 y_width_def=y_width_tightpage;
337 x_offset_def=x_offset_tightpage;
338 y_offset_def=y_offset_tightpage;
339 }
340 if (x_width_def >= 0) { /* extend BBOX */
341 min(x_min,-x_offset_def);
342 max(x_max,x_min + x_width_def);
343 min(y_min,-y_offset_def);
344 max(y_max,y_min + y_width_def);
345 }
346 if (x_width_def <= 0 || option_flags & EXPAND_BBOX) {
347 x_width = x_max-x_min;
348 y_width = y_max-y_min;
349 x_offset = -x_min; /* offset by moving topleft corner */
350 y_offset = -y_min; /* offset by moving topleft corner */
351 } else {
352 x_width=x_width_def;
353 y_width=y_width_def;
354 x_offset=x_offset_def;
355 y_offset=y_offset_def;
356 }
357 DEBUG_PRINT(DEBUG_DVI,("\n IMAGE:\t%dx%d",x_width,y_width));
358 SeekPage(dvi,dvi_pos);
359 CreateImage(x_width,y_width);
360 #ifdef DEBUG
361 DEBUG_PRINT(DEBUG_DVI,("\n@%d PAGE START:\tBOP",dvi_pos->offset));
362 {
363 int i;
364 for (i=0;i<10;i++)
365 DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_pos->count[i]));
366 DEBUG_PRINT(DEBUG_DVI,(" (%d)\n",dvi_pos->count[10]));
367 }
368 #endif
369 Message(REPORT_DEPTH," depth=%d", y_width-y_offset-1);
370 Message(REPORT_HEIGHT," height=%d", y_offset+1);
371 Message(REPORT_WIDTH," width=%d", x_width);
372 page_flags &= ~PAGE_PREVIEW_BOP;
373 DrawPage(x_offset*dvi->conv*shrinkfactor,
374 y_offset*dvi->conv*shrinkfactor);
375 if ( ! (option_flags & MODE_PICKY && page_flags & PAGE_GAVE_WARN )) {
376 WriteImage(dvi->outname,dvi_pos->count[pagecounter]);
377 #ifdef TIMING
378 ++ndone;
379 #endif
380 } else {
381 exitcode=EXIT_FAILURE;
382 Message(BE_NONQUIET,"(page not rendered)");
383 DestroyImage();
384 }
385 Message(BE_NONQUIET,"] ");
386 fflush(stdout);
387 page_flags = 0;
388 dvi_pos=NextPPage(dvi,dvi_pos);
389 }
390 Message(BE_NONQUIET,"\n");
391 ClearPpList();
392 }
393 }
394