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