/* dvi.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, 2019 Jan-Åke Larsson ************************************************************************/ #include "dvipng.h" #ifdef MIKTEX # include # define USLEEP Sleep #else /* MIKTEX */ # ifdef HAVE_LIBGEN_H # include # else # define basename xbasename # endif # ifdef WIN32 # define USLEEP Sleep # include # else # include # define USLEEP usleep # endif /* WIN32 */ #endif /* MIKTEX */ #include #include bool followmode=0; bool DVIFollowToggle(void) { return followmode = ! followmode; } static unsigned char fgetc_follow(FILE* fp) { int got=fgetc(fp),nsleep=1; while(followmode && got==EOF) { USLEEP(nsleep/1310); /* After a few trials, poll every 65536/1310=50 usec */ clearerr(fp); got=fgetc(fp); if (nsleep<50000) nsleep*=2; } if (got==EOF) Fatal("DVI file ends prematurely"); return (unsigned char)got; } static void DVIInit(struct dvi_data* dvi) { int k; unsigned char* pre; struct stat stat; fseek(dvi->filep,0,SEEK_SET); pre=DVIGetCommand(dvi); if (*pre != PRE) { Fatal("PRE does not occur first - are you sure this is a DVI file?"); } k = UNumRead(pre+1,1); DEBUG_PRINT(DEBUG_DVI,("DVI START:\tPRE %d",k)); if (k != DVIFORMAT) { Fatal("DVI format = %d, can only process DVI format %d files", k, DVIFORMAT); } dvi->num = UNumRead(pre+2, 4); dvi->den = UNumRead(pre+6, 4); DEBUG_PRINT(DEBUG_DVI,(" %d/%d",dvi->num,dvi->den)); dvi->mag = UNumRead(pre+10, 4); /*FIXME, see font.c*/ DEBUG_PRINT(DEBUG_DVI,(" %d",dvi->mag)); if ( usermag > 0 && usermag != dvi->mag ) { Warning("DVI magnification of %d over-ridden by user (%ld)", (long)dvi->mag, usermag ); dvi->mag = usermag; } dvi->conv = (1.0/(((double)dvi->num / (double)dvi->den) * ((double)dvi->mag / 1000.0) * ((double)dpi*shrinkfactor/254000.0)))+0.5; DEBUG_PRINT(DEBUG_DVI,(" (%d)",dvi->conv)); k = UNumRead(pre+14,1); DEBUG_PRINT(DEBUG_DVI,(" '%.*s'",k,pre+15)); Message(BE_VERBOSE,"'%.*s' -> %s\n",k,pre+15,dvi->outname); fstat(fileno(dvi->filep), &stat); dvi->mtime = stat.st_mtime; dvi->pagelistp=NULL; dvi->flags = 0; } struct dvi_data* DVIOpen(char* dviname,char* outname) { char* tmpstring; struct dvi_data* dvi; if ((dvi = calloc(1,sizeof(struct dvi_data)))==NULL) Fatal("cannot allocate memory for DVI struct"); dvi->type = DVI_TYPE; dvi->fontnump=NULL; if ((dvi->name = malloc(strlen(dviname)+5))==NULL) Fatal("cannot allocate space for DVI filename"); strcpy(dvi->name, dviname); tmpstring = strrchr(dvi->name, '.'); if (tmpstring == NULL || strcmp(tmpstring,".dvi") != 0) strcat(dvi->name, ".dvi"); if (outname==NULL) { if ((dvi->outname = malloc(strlen(basename(dviname))+7))==NULL) { free(dvi->name); free(dvi); Fatal("cannot allocate space for output filename"); } strcpy(dvi->outname,basename(dviname)); tmpstring = strrchr(dvi->outname, '.'); if (tmpstring != NULL && strcmp(tmpstring,".dvi") == 0) *tmpstring = '\0'; strcat(dvi->outname, "%d.png"); } else { if ((dvi->outname = malloc(strlen(outname)+1))==NULL) { free(dvi->name); free(dvi); Fatal("cannot allocate space for output filename"); } strcpy(dvi->outname,outname); } if ((dvi->filep = fopen(dvi->name,"rb")) == NULL) { /* do not insist on .dvi */ tmpstring = strrchr(dvi->name, '.'); *tmpstring='\0'; dvi->filep = fopen(dvi->name,"rb"); } while((dvi->filep == NULL) && followmode) { USLEEP(50); *tmpstring='.'; if ((dvi->filep = fopen(dvi->name,"rb")) == NULL) { /* do not insist on .dvi */ *tmpstring='\0'; dvi->filep = fopen(dvi->name,"rb"); } } if (dvi->filep == NULL) { free(dvi->name); free(dvi->outname); free(dvi); perror(dviname); exit (EXIT_FAILURE); } DEBUG_PRINT(DEBUG_DVI,("OPEN FILE\t%s", dvi->name)); DVIInit(dvi); return(dvi); } unsigned char* DVIGetCommand(struct dvi_data* dvi) /* This function reads in and stores the next dvi command. */ /* Mmap is not appropriate here, we may want to read from half-written files. */ { static unsigned char* command=NULL; static uint32_t commlen=0; unsigned char *current = command; int length; uint32_t strlength=0; if (commlen==0) { commlen=STRSIZE; if ((current=command=malloc(commlen))==NULL) Fatal("cannot allocate memory for DVI command"); } DEBUG_PRINT(DEBUG_DVI,("\n@%ld ", ftell(dvi->filep))); *(current++) = fgetc_follow(dvi->filep); length = dvi_commandlength[*command]; if (length < 0) Fatal("undefined DVI op-code %d",*command); while(current < command+length) *(current++) = fgetc_follow(dvi->filep); switch (*command) { case XXX4: strlength = *(current - 4); case XXX3: strlength = strlength * 256 + *(current - 3); case XXX2: strlength = strlength * 256 + *(current - 2); case XXX1: strlength = strlength * 256 + *(current - 1); break; case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: strlength = *(current - 1) + *(current - 2); break; case PRE: strlength = *(current - 1); break; } if (strlength > 0) { /* Read string */ if (strlength > UINT32_MAX - (uint32_t)length - 1) Fatal("integer overflow in DVI command length"); if (strlength+1 + (uint32_t)length > commlen) { /* string + command length exceeds that of buffer */ commlen=strlength+1 + (uint32_t)length; if ((command=realloc(command,commlen))==NULL) Fatal("cannot allocate memory for DVI command"); current = command + length; } while(current < command+length+strlength) *(current++) = fgetc_follow(dvi->filep); *current='\0'; } return(command); } bool DVIIsNextPSSpecial(struct dvi_data* dvi) /* This function checks if the next dvi command is a raw PS special */ /* Mmap is not appropriate here, we may want to read from half-written files. */ { long fpos; uint32_t strlength=0; bool israwps=false; DEBUG_PRINT(DEBUG_DVI,("\n CHECKING NEXT DVI COMMAND ")); fpos=ftell(dvi->filep); switch (fgetc_follow(dvi->filep)) { case XXX4: strlength = fgetc_follow(dvi->filep); case XXX3: strlength = strlength * 256 + fgetc_follow(dvi->filep); case XXX2: strlength = strlength * 256 + fgetc_follow(dvi->filep); case XXX1: strlength = strlength * 256 + fgetc_follow(dvi->filep); } if (strlength > 0) { switch(fgetc_follow(dvi->filep)) { case 'p': if (strlength > 2 && fgetc_follow(dvi->filep)=='s' && fgetc_follow(dvi->filep)==':') israwps=true; break; case '"': israwps=true; } } fseek(dvi->filep,fpos,SEEK_SET); return(israwps); } uint32_t CommandLength(unsigned char* command) { /* generally 2^32+5 bytes max, but in practice 32 bit numbers suffice */ uint32_t length=0; length = dvi_commandlength[*command]; switch (*command) { case XXX1: case XXX2: case XXX3: case XXX4: length += UNumRead(command + 1,length - 1); break; case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: length += *(command + length - 1) + *(command + length - 2); break; case PRE: length += *(command + length - 1); break; } return(length); } static void SkipPage(struct dvi_data* dvi) { /* Skip present page */ unsigned char* command; command=DVIGetCommand(dvi); while (*command != EOP) { switch (*command) { case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: DEBUG_PRINT(DEBUG_DVI,("NOSKIP CMD:\t%s", dvi_commands[*command])); FontDef(command,dvi); break; case XXX1: case XXX2: case XXX3: case XXX4: DEBUG_PRINT(DEBUG_DVI,("NOSKIP CMD:\t%s %d", dvi_commands[*command], 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), 0,0); break; case BOP: case PRE: case POST: case POST_POST: Fatal("%s occurs within page", dvi_commands[*command]); break; #ifdef DEBUG default: DEBUG_PRINT(DEBUG_DVI,("SKIP CMD:\t%s", dvi_commands[*command])); #endif } command=DVIGetCommand(dvi); } /* while */ DEBUG_PRINT(DEBUG_DVI,("SKIP CMD:\t%s", dvi_commands[*command])); } static struct page_list* InitPage(struct dvi_data* dvi) { /* Find page start, return pointer to page_list entry if found */ struct page_list* tpagelistp=NULL; unsigned char* command; command=DVIGetCommand(dvi); /* Skip until page start or postamble */ while((*command != BOP) && (*command != POST)) { switch(*command) { case FNT_DEF1: case FNT_DEF2: case FNT_DEF3: case FNT_DEF4: DEBUG_PRINT(DEBUG_DVI,("NOPAGE CMD:\t%s", dvi_commands[*command])); FontDef(command,dvi); break; case NOP: DEBUG_PRINT(DEBUG_DVI,("NOPAGE CMD:\tNOP")); break; default: Fatal("%s occurs outside page", dvi_commands[*command]); } command=DVIGetCommand(dvi); } if ((tpagelistp = malloc(sizeof(struct page_list) +(csp+1-2)*sizeof(struct dvi_color)))==NULL) Fatal("cannot allocate memory for new page entry"); tpagelistp->next = NULL; if ( *command == BOP ) { /* Init page */ int i; DEBUG_PRINT(DEBUG_DVI,("PAGE START:\tBOP")); StoreColorStack(tpagelistp); tpagelistp->offset = ftell(dvi->filep)-45; for (i = 0; i <= 9; i++) { tpagelistp->count[i] = UNumRead(command + 1 + i*4, 4); DEBUG_PRINT(DEBUG_DVI,(" %d",tpagelistp->count[i])); } if (dvi->pagelistp==NULL) tpagelistp->count[10] = 1; else tpagelistp->count[10] = dvi->pagelistp->count[10]+1; DEBUG_PRINT(DEBUG_DVI,(" (%d)", tpagelistp->count[10])); } else { DEBUG_PRINT(DEBUG_DVI,("DVI END:\tPOST")); tpagelistp->offset = ftell(dvi->filep)-1; tpagelistp->count[0] = PAGE_POST; /* POST */ tpagelistp->count[10] = PAGE_POST; /* POST */ } return(tpagelistp); } int SeekPage(struct dvi_data* dvi, struct page_list* page) { ReadColorStack(page); return(fseek(dvi->filep, page->offset+((page->count[0]==PAGE_POST) ? 1L : 45L), SEEK_SET)); } struct page_list* NextPage(struct dvi_data* dvi, struct page_list* page) { struct page_list* tpagelistp; /* if page points to POST there is no next page */ if (page!=NULL && page->count[0]==PAGE_POST) return(NULL); /* If we have read past the last page in our current list or the * list is empty, sneak a look at the next page */ if (dvi->pagelistp==NULL || dvi->pagelistp->offset+45L < ftell(dvi->filep)) { tpagelistp=dvi->pagelistp; dvi->pagelistp=InitPage(dvi); dvi->pagelistp->next=tpagelistp; } if (page!=dvi->pagelistp) { /* also works if page==NULL, we'll get the first page then */ tpagelistp=dvi->pagelistp; while(tpagelistp!=NULL && tpagelistp->next!=page) tpagelistp=tpagelistp->next; } else { /* dvi->pagelistp points to the last page we've read so far, * the last page that we know where it is, so to speak * So look at the next */ (void)SeekPage(dvi,dvi->pagelistp); SkipPage(dvi); tpagelistp=dvi->pagelistp; dvi->pagelistp=InitPage(dvi); dvi->pagelistp->next=tpagelistp; tpagelistp=dvi->pagelistp; } return(tpagelistp); } struct page_list* PrevPage(struct dvi_data* dvi, struct page_list* page) { return(page->next); } struct page_list* FindPage(struct dvi_data* dvi, int32_t pagenum, bool abspage) /* Find first page of certain number, absolute number if abspage is set */ { struct page_list* page=NextPage(dvi, NULL); if (pagenum==PAGE_LASTPAGE || pagenum==PAGE_POST) { while(page!=NULL && page->count[0]!=PAGE_POST) page=NextPage(dvi,page); if (pagenum==PAGE_LASTPAGE) page=PrevPage(dvi,page); } else if (pagenum!=PAGE_FIRSTPAGE) while(page != NULL && pagenum != page->count[abspage ? 0 : 10]) page=NextPage(dvi,page); return(page); } static void DelPageList(struct dvi_data* dvi) { struct page_list* temp; /* Delete the page list */ temp=dvi->pagelistp; while(temp!=NULL) { dvi->pagelistp=dvi->pagelistp->next; free(temp); temp=dvi->pagelistp; } } void DVIClose(struct dvi_data* dvi) { if (dvi!=NULL) { fclose(dvi->filep); DelPageList(dvi); ClearPSHeaders(); free(dvi->outname); free(dvi->name); free(dvi); } } bool DVIReOpen(struct dvi_data* dvi) { struct stat stat; fstat(fileno(dvi->filep), &stat); if (dvi->mtime != stat.st_mtime) { fclose(dvi->filep); dvi->filep=NULL; DelPageList(dvi); ClearPSHeaders(); while(((dvi->filep = fopen(dvi->name,"rb")) == NULL) && followmode) { USLEEP(50); } if (dvi->filep == NULL) { perror(dvi->name); exit(EXIT_FAILURE); } Message(PARSE_STDIN,"Reopened file\n"); DEBUG_PRINT(DEBUG_DVI,("\nREOPEN FILE\t%s", dvi->name)); DVIInit(dvi); return(true); } return(false); }