/* draw.c */ /************************************************************************ Part of the dvipng distribution This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . Copyright (C) 2002-2015 Jan-Åke Larsson ************************************************************************/ #include "dvipng.h" #ifdef DEBUG #include /* isprint */ #endif struct stack_entry { dviunits h, v, w, x, y, z; /* stack entry */ subpixels hh,vv; } stack[STACK_SIZE+1]; /* stack + space for current pos */ struct stack_entry* dvi_stack=stack; #define MAXDRIFT 1 #define CHECK_MAXDRIFT(x,xx) \ if ( xx-PIXROUND(x,dvi->conv*shrinkfactor) < -MAXDRIFT ) { \ DEBUG_PRINT(DEBUG_DVI,(" add 1 to")); \ xx += 1; \ } \ if ( xx-PIXROUND(x,dvi->conv*shrinkfactor) > MAXDRIFT ) { \ DEBUG_PRINT(DEBUG_DVI,(" sub 1 to")); \ xx -= 1; \ } \ if (PIXROUND(dvi_stack->h,dvi->conv*shrinkfactor) != dvi_stack->hh \ || PIXROUND(dvi_stack->v,dvi->conv*shrinkfactor) != dvi_stack->vv)\ DEBUG_PRINT(DEBUG_DVI, \ (" drift (%d,%d)", \ dvi_stack->hh-PIXROUND(dvi_stack->h,dvi->conv*shrinkfactor), \ dvi_stack->vv-PIXROUND(dvi_stack->v,dvi->conv*shrinkfactor))); #define MoveRight(x) \ temp=x; dvi_stack->h += temp; \ if ( currentfont==NULL \ || temp > currentfont->s/6 || temp < -currentfont->s/6*4 ) \ dvi_stack->hh = PIXROUND(dvi_stack->h,dvi->conv*shrinkfactor); \ else \ dvi_stack->hh += PIXROUND( temp,dvi->conv*shrinkfactor ); \ CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh) #define MoveDown(x) \ temp=x; dvi_stack->v += temp; \ if ( currentfont==NULL \ || temp > currentfont->s/6*5 || temp < currentfont->s/6*(-5) ) \ dvi_stack->vv = PIXROUND(dvi_stack->v,dvi->conv*shrinkfactor); \ else \ dvi_stack->vv += PIXROUND( temp,dvi->conv*shrinkfactor ); \ CHECK_MAXDRIFT(dvi_stack->v,dvi_stack->vv) #define DO_VFCONV(a) ((((struct font_entry*) parent)->type==DVI_TYPE)?a:\ (dviunits)((int64_t) a * ((struct font_entry*) parent)->s / (1 << 20))) dviunits SetChar(int32_t c) { struct char_entry* ptr=NULL; if (currentfont==NULL) Fatal("faulty DVI, trying to set character from null font"); if (c<0 || c>LASTFNTCHAR) { Warning("glyph index out of range (%d), skipping",c); return(0); } ptr=currentfont->chr[c]; if (ptr==NULL) { Warning("unable to draw glyph %d, skipping",c); return(0); } #ifdef DEBUG switch (currentfont->type) { case FONT_TYPE_VF: DEBUG_PRINT(DEBUG_DVI,("\n VF CHAR:\t")); break; case FONT_TYPE_PK: DEBUG_PRINT(DEBUG_DVI,("\n PK CHAR:\t")); break; case FONT_TYPE_FT: DEBUG_PRINT(DEBUG_DVI,("\n FT CHAR:\t")); break; default: DEBUG_PRINT(DEBUG_DVI,("\n NO CHAR:\t")) } if (debug & DEBUG_DVI && c>=0 && c<=UCHAR_MAX && isprint(c)) DEBUG_PRINT(DEBUG_DVI,("'%c' ",c)); DEBUG_PRINT(DEBUG_DVI,("%d at (%d,%d) tfmw %d", c, dvi_stack->hh,dvi_stack->vv,ptr?ptr->tfmw:0)); #endif if (currentfont->type==FONT_TYPE_VF) { return(SetVF(ptr)); } else { if (ptr->data == NULL) switch(currentfont->type) { case FONT_TYPE_PK: LoadPK(c, ptr); break; #ifdef HAVE_FT2 case FONT_TYPE_FT: LoadFT(c, ptr); break; #endif default: Fatal("undefined fonttype %d",currentfont->type); } if (page_imagep != NULL) return(SetGlyph(ptr, dvi_stack->hh, dvi_stack->vv)); else { /* Expand bounding box if necessary */ min(x_min,dvi_stack->hh - ptr->xOffset/shrinkfactor); min(y_min,dvi_stack->vv - ptr->yOffset/shrinkfactor); max(x_max,dvi_stack->hh - ptr->xOffset/shrinkfactor + ptr->w); max(y_max,dvi_stack->vv - ptr->yOffset/shrinkfactor + ptr->h); return(ptr->tfmw); } } return(0); } void DrawCommand(unsigned char* command, void* parent /* dvi/vf */) /* To be used both in plain DVI drawing and VF drawing */ { dviunits temp; if (/*command >= SETC_000 &&*/ *command <= SETC_127) { temp = SetChar((int32_t)*command); dvi_stack->h += temp; dvi_stack->hh += PIXROUND(temp,dvi->conv*shrinkfactor); CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh); } else if (*command >= FONT_00 && *command <= FONT_63) { SetFntNum((int32_t)*command - FONT_00,parent); } else switch (*command) { case PUT1: case PUT2: case PUT3: case PUT4: DEBUG_PRINT(DEBUG_DVI,(" %d", UNumRead(command+1, dvi_commandlength[*command]-1))); (void) SetChar(UNumRead(command+1, dvi_commandlength[*command]-1)); break; case SET1: case SET2: case SET3: case SET4: DEBUG_PRINT(DEBUG_DVI,(" %d", UNumRead(command+1, dvi_commandlength[*command]-1))); { temp = SetChar(UNumRead(command+1, dvi_commandlength[*command]-1)); dvi_stack->h += temp; dvi_stack->hh += PIXROUND(temp,dvi->conv*shrinkfactor); CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh); } break; case SET_RULE: DEBUG_PRINT(DEBUG_DVI,(" %d %d", UNumRead(command+1, 4), UNumRead(command+5, 4))); temp = SetRule(DO_VFCONV(UNumRead(command+1, 4)), DO_VFCONV(UNumRead(command+5, 4)), dvi_stack->hh, dvi_stack->vv); dvi_stack->h += temp; dvi_stack->hh += PIXROUND(temp,dvi->conv*shrinkfactor); CHECK_MAXDRIFT(dvi_stack->h,dvi_stack->hh); break; case PUT_RULE: DEBUG_PRINT(DEBUG_DVI,(" %d %d", UNumRead(command+1, 4), UNumRead(command+5, 4))); (void) SetRule(DO_VFCONV(UNumRead(command+1, 4)), DO_VFCONV(UNumRead(command+5, 4)), dvi_stack->hh, dvi_stack->vv); break; case BOP: Fatal("BOP occurs within page"); break; case EOP: break; case PUSH: /* is next item on stack? */ if (dvi_stack == &stack[STACK_SIZE-1]) Fatal("DVI stack overflow"); { struct stack_entry *next=dvi_stack+1; next->h = dvi_stack->h; next->v = dvi_stack->v; next->w = dvi_stack->w; next->x = dvi_stack->x; next->y = dvi_stack->y; next->z = dvi_stack->z; next->hh = dvi_stack->hh; next->vv = dvi_stack->vv; dvi_stack=next; } break; case POP: if (dvi_stack == stack) Fatal("DVI stack underflow"); dvi_stack--; break; case RIGHT1: case RIGHT2: case RIGHT3: case RIGHT4: DEBUG_PRINT(DEBUG_DVI,(" %d", SNumRead(command+1, dvi_commandlength[*command]-1))); MoveRight(DO_VFCONV(SNumRead(command+1, dvi_commandlength[*command]-1))); break; case W1: case W2: case W3: case W4: dvi_stack->w = SNumRead(command+1, dvi_commandlength[*command]-1); DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->w)); case W0: MoveRight(DO_VFCONV(dvi_stack->w)); break; case X1: case X2: case X3: case X4: dvi_stack->x = SNumRead(command+1, dvi_commandlength[*command]-1); DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->x)); case X0: MoveRight(DO_VFCONV(dvi_stack->x)); break; case DOWN1: case DOWN2: case DOWN3: case DOWN4: DEBUG_PRINT(DEBUG_DVI,(" %d", SNumRead(command+1, dvi_commandlength[*command]-1))); MoveDown(DO_VFCONV(SNumRead(command+1, dvi_commandlength[*command]-1))); break; case Y1: case Y2: case Y3: case Y4: dvi_stack->y = SNumRead(command+1, dvi_commandlength[*command]-1); DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->y)); case Y0: MoveDown(DO_VFCONV(dvi_stack->y)); break; case Z1: case Z2: case Z3: case Z4: dvi_stack->z = SNumRead(command+1, dvi_commandlength[*command]-1); DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_stack->z)); case Z0: MoveDown(DO_VFCONV(dvi_stack->z)); break; case FNT1: case FNT2: case FNT3: case FNT4: DEBUG_PRINT(DEBUG_DVI,(" %d", UNumRead(command+1, dvi_commandlength[*command]-1))); SetFntNum(UNumRead(command+1, dvi_commandlength[*command]-1),parent); break; case XXX1: case XXX2: case XXX3: case XXX4: DEBUG_PRINT(DEBUG_DVI,(" %d", UNumRead(command+1, dvi_commandlength[*command]-1))); SetSpecial((char*)command + dvi_commandlength[*command], (char*)command + dvi_commandlength[*command] +UNumRead(command+1, dvi_commandlength[*command]-1), dvi_stack->hh,dvi_stack->vv); break; case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: if (((struct font_entry*)parent)->type==DVI_TYPE) { FontDef(command, parent); } else { Fatal("%s within VF macro from %s",dvi_commands[*command], ((struct font_entry*)parent)->n); } break; case PRE: case POST: case POST_POST: Fatal("%s occurs within page",dvi_commands[*command]); break; case NOP: break; default: Fatal("%s is an undefined command",dvi_commands[*command]); break; } } void BeginVFMacro(struct font_entry* currentvf) { struct stack_entry *next=dvi_stack+1; if (dvi_stack == &stack[STACK_SIZE-1]) Fatal("DVI stack overflow"); next->h = dvi_stack->h; next->v = dvi_stack->v; next->w = next->x = next->y = next->z = 0; next->hh = dvi_stack->hh; next->vv = dvi_stack->vv; dvi_stack = next; DEBUG_PRINT(DEBUG_DVI,("\n START VF:\tPUSH, W = X = Y = Z = 0")); SetFntNum(currentvf->defaultfont,currentvf); } void EndVFMacro(void) { if (dvi_stack == stack) Fatal("DVI stack underflow"); dvi_stack--; DEBUG_PRINT(DEBUG_DVI,("\n END VF:\tPOP ")); } static void DrawPage(dviunits hoffset, dviunits voffset) /* To be used after having read BOP and will exit cleanly when * encountering EOP. */ { unsigned char* command; /* current command */ dvi_stack->h = hoffset; dvi_stack->v = voffset; dvi_stack->w = dvi_stack->x = dvi_stack->y = dvi_stack->z = 0; dvi_stack->hh = PIXROUND( dvi_stack->h , dvi->conv*shrinkfactor ); dvi_stack->vv = PIXROUND( dvi_stack->v , dvi->conv*shrinkfactor ); currentfont = NULL; /* No default font */ command=DVIGetCommand(dvi); DEBUG_PRINT(DEBUG_DVI,("DRAW CMD:\t%s", dvi_commands[*command])); while (*command != EOP) { DrawCommand(command,dvi); command=DVIGetCommand(dvi); DEBUG_PRINT(DEBUG_DVI,("DRAW CMD:\t%s", dvi_commands[*command])); } } void DrawPages(void) { struct page_list *dvi_pos; pixels x_width,y_width,x_offset,y_offset; int pagecounter=(option_flags & DVI_PAGENUM)?0:10; dvi_pos=NextPPage(dvi,NULL); if (dvi_pos!=NULL) { while(dvi_pos!=NULL) { SeekPage(dvi,dvi_pos); Message(BE_NONQUIET,"[%d", dvi_pos->count[pagecounter]); if (dvi_pos->count[pagecounter]!=dvi_pos->count[0]) Message(BE_NONQUIET," (%d)", dvi_pos->count[0]); x_max = y_max = INT32_MIN; x_min = y_min = INT32_MAX; DrawPage((dviunits)0,(dviunits)0); /* Store background color. Background color of a page is given by the color at EOP rather than the color at BOP. */ StoreBackgroundColor(dvi_pos); /* Store pagesize */ if (dvi->flags & DVI_PREVIEW_LATEX_TIGHTPAGE) { x_width_def=x_width_tightpage; y_width_def=y_width_tightpage; x_offset_def=x_offset_tightpage; y_offset_def=y_offset_tightpage; } if (x_width_def >= 0) { /* extend BBOX */ min(x_min,-x_offset_def); max(x_max,x_min + x_width_def); min(y_min,-y_offset_def); max(y_max,y_min + y_width_def); } if (x_width_def <= 0 || option_flags & EXPAND_BBOX) { x_width = x_max-x_min; y_width = y_max-y_min; x_offset = -x_min; /* offset by moving topleft corner */ y_offset = -y_min; /* offset by moving topleft corner */ } else { x_width=x_width_def; y_width=y_width_def; x_offset=x_offset_def; y_offset=y_offset_def; } DEBUG_PRINT(DEBUG_DVI,("\n IMAGE:\t%dx%d",x_width,y_width)); SeekPage(dvi,dvi_pos); CreateImage(x_width,y_width); #ifdef DEBUG DEBUG_PRINT(DEBUG_DVI,("\n@%d PAGE START:\tBOP",dvi_pos->offset)); { int i; for (i=0;i<10;i++) DEBUG_PRINT(DEBUG_DVI,(" %d",dvi_pos->count[i])); DEBUG_PRINT(DEBUG_DVI,(" (%d)\n",dvi_pos->count[10])); } #endif Message(REPORT_DEPTH," depth=%d", y_width-y_offset-1); Message(REPORT_HEIGHT," height=%d", y_offset+1); Message(REPORT_WIDTH," width=%d", x_width); page_flags &= ~PAGE_PREVIEW_BOP; DrawPage(x_offset*dvi->conv*shrinkfactor, y_offset*dvi->conv*shrinkfactor); if ( ! (option_flags & MODE_PICKY && page_flags & PAGE_GAVE_WARN )) { WriteImage(dvi->outname,dvi_pos->count[pagecounter]); #ifdef TIMING ++ndone; #endif } else { exitcode=EXIT_FAILURE; Message(BE_NONQUIET,"(page not rendered)"); DestroyImage(); } Message(BE_NONQUIET,"] "); fflush(stdout); page_flags = 0; dvi_pos=NextPPage(dvi,dvi_pos); } Message(BE_NONQUIET,"\n"); ClearPpList(); } }