/* special.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" #ifndef MIKTEX #ifndef WIN32 #include #else /* WIN32 */ #include #include #include #ifndef pipe #define pipe(p) _pipe(p, 65536, O_BINARY | _O_NOINHERIT) #endif #ifndef snprintf #define snprintf _snprintf #endif #endif /* WIN32 */ #endif #define SKIPSPACES(s) while(s && *s==' ' && *s!='\0') s++ /* PostScript can come as a string (headers and raw specials) or a memory-mapped file (headers and included EPS figures). */ struct pscode { struct pscode* next; char* special; /* complete special */ const char* code; /* PS string, null if a file */ char* filename; /* file name, null if a string */ const char* postcode; /* post PS string */ struct filemmap fmmap; /* file mmap */ }; struct pscode* psheaderp=NULL; /* static, DVI-specific header list */ static void PSCodeInit(struct pscode *entry, char *special) { entry->next=NULL; entry->special=special; entry->code=NULL; entry->filename=NULL; entry->postcode=NULL; entry->fmmap.data=NULL; if (special==NULL) return; if (strncmp(special,"header=",7)==0) entry->filename=special+7; else if (strncmp(special,"ps:: plotfile ",14)==0) entry->filename=special+14; else if (special[0]=='"' || special[0]=='!') entry->code=special+1; else if (strncmp(special,"ps::[begin]",11)==0) entry->code=special+11; else if (strncmp(special,"ps::[end]",9)==0) entry->code=special+9; else if (strncmp(special,"ps::",4)==0) entry->code=special+4; else if (strncmp(special,"ps:",3)==0) entry->code=special+3; else entry->code=special; #ifdef DEBUG if (entry->code!=NULL) DEBUG_PRINT(DEBUG_GS,(" '%s'",entry->code)); if (entry->filename!=NULL) DEBUG_PRINT(DEBUG_GS,(" {%s}",entry->filename)); if (entry->postcode!=NULL) DEBUG_PRINT(DEBUG_GS,(" '%s'",entry->postcode)); #endif } void ClearPSHeaders(void) { struct pscode *temp=psheaderp; while(temp!=NULL) { psheaderp=psheaderp->next; if (temp->fmmap.data!=NULL) UnMmapFile(&(temp->fmmap)); free(temp); temp=psheaderp; } } static void writepscode(FILE* psstream,struct pscode* pscodep) { while (pscodep!=NULL) { if (pscodep->code!=NULL) { fputs(pscodep->code,psstream); putc('\n',psstream); DEBUG_PRINT(DEBUG_GS,("\n PS CODE:\t%s",pscodep->code)); } if (pscodep->filename!=NULL && pscodep->fmmap.data==NULL) { char* filepath= kpse_find_file(pscodep->filename,kpse_tex_ps_header_format,false); if (filepath==NULL) { Warning("Cannot find PostScript file %s, ignored", pscodep->filename); page_flags |= PAGE_GAVE_WARN; } else if (MmapFile(filepath,&(pscodep->fmmap))) { Warning("PostScript file %s unusable, ignored", pscodep->filename); page_flags |= PAGE_GAVE_WARN; } } if (pscodep->fmmap.data!=NULL) { unsigned char* position; DEBUG_PRINT(DEBUG_GS,("\n PS FILE:\t%s",pscodep->filename)); position=(unsigned char*)pscodep->fmmap.data; while(position < (unsigned char*)pscodep->fmmap.data + pscodep->fmmap.size) { putc(*position,psstream); position++; } } if (pscodep->postcode!=NULL) { fputs(pscodep->postcode,psstream); putc('\n',psstream); DEBUG_PRINT(DEBUG_GS,("\n PS POST CODE:\t%s",pscodep->postcode)); } pscodep=pscodep->next; } } static gdImagePtr ps2png(struct pscode* pscodep, const char *device, int hresolution, int vresolution, int llx, int lly, int urx, int ury, int bgred, int bggreen, int bgblue) { int pspipe[2], pngpipe[2]; #define READ_END 0 #define WRITE_END 1 FILE *psstream=NULL, *pngstream=NULL; char resolution[STRSIZE]; /* char devicesize[STRSIZE]; */ gdImagePtr psimage=NULL; #ifndef MIKTEX #ifndef WIN32 pid_t pid; #else /* WIN32 */ unsigned long nexitcode = STILL_ACTIVE; HANDLE hchild; int savestdin, savestdout; #endif /* WIN32 */ #else /* MIKTEX */ HANDLE hPngStream; HANDLE hPsStream; HANDLE hStdErr; PROCESS_INFORMATION pi; _TCHAR szCommandLine[2048]; _TCHAR szGsPath[_MAX_PATH]; #define GS_PATH szGsPath #define fdopen _tfdopen #define close _close #endif /* MIKTEX */ snprintf(resolution,STRSIZE,"-r%dx%d",hresolution,vresolution); /* Future extension for \rotatebox status=sprintf(devicesize, "-g%dx%d", //(int)((sin(atan(1.0))+1)* (urx - llx)*hresolution/72,//), //(int)((sin(atan(1.0))+1)* (ury - lly)*vresolution/72);//); */ DEBUG_PRINT(DEBUG_GS, ("\n GS CALL:\t%s %s %s %s %s %s %s %s %s %s %s",/* %s", */ GS_PATH, device, resolution, /*devicesize,*/ "-dBATCH", "-dNOPAUSE", "-q", "-sOutputFile=-", "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4", (option_flags & NO_GSSAFER) ? "-": "-dSAFER", (option_flags & NO_GSSAFER) ? "": "- ")); #ifndef MIKTEX if (pipe(pspipe) || pipe(pngpipe)) return(NULL); #ifndef WIN32 /* We have fork: execute gs in child */ pid = fork(); if (pid == 0) { /* Child, execute gs. */ close(pspipe[WRITE_END]); dup2(pspipe[READ_END], STDIN_FILENO); close(pspipe[READ_END]); close(pngpipe[READ_END]); dup2(pngpipe[WRITE_END], STDOUT_FILENO); close(pngpipe[WRITE_END]); execlp(GS_PATH, GS_PATH, device, resolution, /*devicesize,*/ "-dBATCH", "-dNOPAUSE", "-q", "-sOutputFile=-", "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4", (option_flags & NO_GSSAFER) ? "-": "-dSAFER", (option_flags & NO_GSSAFER) ? NULL: "-", NULL); _exit (EXIT_FAILURE); } #else /* WIN32 */ /* No fork but spawn: execute gs in present process environment. Save fileno's, attach pipes to this process' stdin and stdout. */ savestdin = _dup(fileno(stdin)); _dup2(pspipe[READ_END], fileno(stdin)); savestdout = _dup(fileno(stdout)); _dup2(pngpipe[WRITE_END], fileno(stdout)); if ((hchild= (HANDLE)_spawnlp(_P_NOWAIT, GS_PATH, GS_PATH, device, resolution, "-dBATCH", "-dNOPAUSE", "-q", "-sOutputFile=-", "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4", (option_flags & NO_GSSAFER) ? "-": "-dSAFER", (option_flags & NO_GSSAFER) ? NULL : "-", NULL))==0) return NULL; #endif /* WIN32 */ close(pspipe[READ_END]); close(pngpipe[WRITE_END]); #else /* MIKTEX */ /* No fork but miktex_start_process3: execute gs using that. Attach file descriptors to that process' stdin and stdout. */ if (! miktex_find_miktex_executable("mgs.exe", szGsPath)) { Warning("Ghostscript could not be found"); return(NULL); } snprintf(szCommandLine,2048,"\"%s\" %s %s %s %s %s %s %s %s %s %s",/* %s",*/ szGsPath, device, resolution, /*devicesize,*/ "-dBATCH", "-dNOPAUSE", "-q", "-sOutputFile=-", "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4", (option_flags & NO_GSSAFER) ? "-": "-dSAFER", (option_flags & NO_GSSAFER) ? "": "-"); if (! miktex_start_process_3(szCommandLine, &pi, INVALID_HANDLE_VALUE, &hPsStream, &hPngStream, &hStdErr, 0)) { Warning("Ghostscript could not be started"); return(NULL); } CloseHandle (pi.hThread); pspipe[WRITE_END] = _open_osfhandle((intptr_t)hPsStream, _O_WRONLY); pngpipe[READ_END] = _open_osfhandle((intptr_t)hPngStream, _O_RDONLY); #endif /* MIKTEX */ if (pspipe[WRITE_END] >= 0) { if ((psstream=fdopen(pspipe[WRITE_END],"wb")) == NULL) close(pspipe[WRITE_END]); else { writepscode(psstream,psheaderp); /* Page size */ DEBUG_PRINT(DEBUG_GS,("\n PS CODE:\t<>setpagedevice", urx - llx, ury - lly,llx,lly)); fprintf(psstream, "<>setpagedevice\n", urx - llx, ury - lly,llx,lly); /* Background color */ if ( bgred < 255 || bggreen < 255 || bgblue < 255 ) { DEBUG_PRINT(DEBUG_GS,("\n PS CODE:\tgsave %f %f %f setrgbcolor clippath fill grestore", bgred/255.0, bggreen/255.0, bgblue/255.0)); fprintf(psstream, "gsave %f %f %f setrgbcolor clippath fill grestore\n", bgred/255.0, bggreen/255.0, bgblue/255.0); } writepscode(psstream,pscodep); fclose(psstream); } } if (pngpipe[READ_END] >= 0) { if((pngstream=fdopen(pngpipe[READ_END],"rb")) == NULL) close(pngpipe[READ_END]); else { psimage = gdImageCreateFromPng(pngstream); fclose(pngstream); } } #ifndef MIKTEX #ifndef WIN32 /* Wait for child */ waitpid(pid,NULL,0); #else /* Wait for spawned process, restore stdin and stdout */ while(nexitcode == STILL_ACTIVE) GetExitCodeProcess((HANDLE)hchild, &nexitcode); CloseHandle((HANDLE)hchild); _dup2(savestdin, fileno(stdin)); _dup2(savestdout, fileno(stdout)); close(savestdin); close(savestdout); #endif /* WIN32 */ #else /* MIKTEX */ /* Close miktex process */ CloseHandle(pi.hProcess); #endif /* MIKTEX */ #ifdef DEBUG if (psimage == NULL) { DEBUG_PRINT(DEBUG_GS,("\n GS OUTPUT:\tNO IMAGE ")); } else { DEBUG_PRINT(DEBUG_GS,("\n GS OUTPUT:\t%dx%d image ", gdImageSX(psimage),gdImageSY(psimage))); } #endif return psimage; } static gdImagePtr rescale(gdImagePtr psimage, int pngwidth, int pngheight) { gdImagePtr scaledimage=psimage; /* Rescale unless correct size */ if (psimage!=NULL && gdImageSX(psimage)!=pngwidth && gdImageSY(psimage)!=pngheight) { DEBUG_PRINT(DEBUG_DVI, ("\n RESCALE INCLUDED BITMAP \t(%d,%d) -> (%d,%d)", gdImageSX(psimage),gdImageSY(psimage), pngwidth,pngheight)); #ifdef HAVE_GDIMAGECREATETRUECOLOR scaledimage=gdImageCreateTrueColor(pngwidth,pngheight); /* Copy with overwrite, remember that this is the rescaled source image. The real target has alpha blending on. */ gdImageAlphaBlending(scaledimage,0); gdImageCopyResampled(scaledimage,psimage,0,0,0,0, pngwidth,pngheight, gdImageSX(psimage),gdImageSY(psimage)); #else scaledimage=gdImageCreate(pngwidth,pngheight); gdImageCopyResized(scaledimage,psimage,0,0,0,0, pngwidth,pngheight, gdImageSX(psimage),gdImageSY(psimage)); #endif gdImageDestroy(psimage); } return(scaledimage); } static void newpsheader(const char* special) { struct pscode* tmp; char* txt; if (psheaderp==NULL && strcmp(special,"header=tex.pro")!=0) { newpsheader("header=tex.pro"); newpsheader("header=color.pro"); newpsheader("header=special.pro"); } if (strcmp(special+strlen(special)-4,".xcp")==0 && strncmp(special,"header=",7)==0) InitXColorPrologue(special+7); if (strncmp(special,"! /pgfH",7)==0) newpsheader("! TeXDict begin"); if (psheaderp==NULL) { if ((tmp=psheaderp=malloc(sizeof(struct pscode)))==NULL) Fatal("cannot malloc space for PostScript header struct"); } else { /* No duplicates. This still misses pre=..., because we still change that. To be fixed */ tmp=psheaderp; if (strcmp(tmp->special,special)==0) return; while(tmp->next!=NULL) { tmp=tmp->next; if (strcmp(tmp->special,special)==0) return; } if ((tmp->next=malloc(sizeof(struct pscode)))==NULL) Fatal("cannot malloc space for PostScript header struct"); tmp=tmp->next; } DEBUG_PRINT(DEBUG_GS,("\n PS HEADER ")); if ((txt=malloc(strlen(special)+1))==NULL) Fatal("cannot malloc space for PostScript header"); strcpy(txt,special); PSCodeInit(tmp,txt); } /*********************************************************************/ /**************************** SetSpecial ***************************/ /*********************************************************************/ void SetSpecial(char *start, char *end, int32_t hh, int32_t vv) /* interpret a \special command, made up of keyword=value pairs, * or !header or ps:raw_PostScript */ { char *buffer,*special; if ((buffer=malloc(end-start+1))==NULL) Fatal("Cannot allocate space for special"); special=memcpy(buffer,start,end-start); special[end-start]='\0'; DEBUG_PRINT(DEBUG_DVI,(" '%s'",special)); SKIPSPACES(special); /********************** Color specials ***********************/ if (strncmp(special,"background ",11)==0) { background(special+11); free(buffer); return; } if (strncmp(special,"color ",6)==0) { special+=6; SKIPSPACES(special); if (strncmp(special,"push ",5)==0) { pushcolor(special+5); } else { if (strcmp(special,"pop")==0) popcolor(); else resetcolorstack(special); } free(buffer); return; } /******************* Image inclusion ********************/ /* Needed tests for regression: PNG, GIF, JPEG and EPS inclusion, * for different gd versions */ if (strncmp(special,"PSfile=",7)==0) { /* PSfile */ char* psname = special+7; int llx=0,lly=0,urx=0,ury=0,rwi=0,rhi=0; bool clip=false; int hresolution,vresolution; int pngheight,pngwidth; /* Remove quotation marks around filename. If no quotation marks, use first word as filename */ if (*psname=='"') { psname++; special=strrchr(psname,'"'); } else { special=strchr(psname,' '); } if (special!=NULL) { *special='\0'; special++; } /* Retrieve parameters */ SKIPSPACES(special); while(special && *special) { if (strncmp(special,"llx=",4)==0) llx = strtol(special+4,&special,10); else if (strncmp(special,"lly=",4)==0) lly = strtol(special+4,&special,10); else if (strncmp(special,"urx=",4)==0) urx = strtol(special+4,&special,10); else if (strncmp(special,"ury=",4)==0) ury = strtol(special+4,&special,10); else if (strncmp(special,"rwi=",4)==0) rwi = strtol(special+4,&special,10); else if (strncmp(special,"rhi=",4)==0) rhi = strtol(special+4,&special,10); else if (strncmp(special,"clip",4)==0) {clip = true; special=special+4;} while (*special && *special!=' ') special++; SKIPSPACES(special); } /* Calculate resolution, and use our base resolution as a fallback. */ /* The factor 10 is magic, the dvips graphicx driver needs this. */ hresolution = ((dpi*rwi+urx-llx-1)/(urx - llx)+9)/10; vresolution = ((dpi*rhi+ury-lly-1)/(ury - lly)+9)/10; if (vresolution==0) vresolution = hresolution; if (hresolution==0) hresolution = vresolution; if (hresolution==0) hresolution = vresolution = dpi; /* Convert from postscript 72 dpi resolution to our given resolution */ pngwidth = (dpi*rwi+719)/720; /* +719: round up */ pngheight = (dpi*rhi+719)/720; if (pngwidth==0) pngwidth = ((dpi*rhi*(urx-llx)+ury-lly-1)/(ury-lly)+719)/720; if (pngheight==0) pngheight = ((dpi*rwi*(ury-lly)+urx-llx-1)/(urx-llx)+719)/720; if (pngheight==0) { pngwidth = (dpi*(urx-llx)+71)/72; pngheight = (dpi*(ury-lly)+71)/72; } if (page_imagep != NULL) { /* Draw into image */ struct pscode image; gdImagePtr psimage=NULL; #ifndef HAVE_GDIMAGECREATEFROMPNGPTR FILE* psstream; #endif PSCodeInit(&image, NULL); image.filename=kpse_find_file(psname,kpse_pict_format,0); if (MmapFile(image.filename,&(image.fmmap)) || image.fmmap.size==0) { Warning("Image file %s unusable, image will be left blank", image.filename); page_flags |= PAGE_GAVE_WARN; free(buffer); return; } Message(BE_NONQUIET," <%s",psname); switch ((unsigned char)*image.fmmap.data) { case 0x89: /* PNG magic: "\211PNG\r\n\032\n" */ DEBUG_PRINT(DEBUG_DVI,("\n INCLUDE PNG \t%s",image.filename)); #ifdef HAVE_GDIMAGECREATEFROMPNGPTR psimage=gdImageCreateFromPngPtr(image.fmmap.size,image.fmmap.data); #else psstream=fopen(image.filename,"rb"); psimage=gdImageCreateFromPng(psstream); fclose(psstream); #endif psimage=rescale(psimage,pngwidth,pngheight); break; case 'G': /* GIF magic: "GIF87" or "GIF89" */ DEBUG_PRINT(DEBUG_DVI,("\n INCLUDE GIF \t%s",image.filename)); #ifdef HAVE_GDIMAGEGIF psimage=rescale(gdImageCreateFromGifPtr(image.fmmap.size, image.fmmap.data), pngwidth,pngheight); #else DEBUG_PRINT(DEBUG_DVI,(" (NO GIF DECODER)")); #endif break; case 0xff: /* JPEG magic: 0xffd8 */ DEBUG_PRINT(DEBUG_DVI,("\n INCLUDE JPEG \t%s",image.filename)); #ifdef HAVE_GDIMAGECREATEFROMJPEG #ifdef HAVE_GDIMAGECREATEFROMPNGPTR psimage=gdImageCreateFromJpegPtr(image.fmmap.size,image.fmmap.data); #else psstream=fopen(image.filename,"rb"); psimage=gdImageCreateFromJpeg(psstream); fclose(psstream); #endif psimage=rescale(psimage,pngwidth,pngheight); #else DEBUG_PRINT(DEBUG_DVI,(" (NO JPEG DECODER)")); #endif break; default: /* Default, PostScript magic: "%!PS-Adobe" */ if (option_flags & NO_GHOSTSCRIPT) { Warning("GhostScript calls disallowed by --nogs" ); page_flags |= PAGE_GAVE_WARN; } else { /* Ensure one (and only one) showpage */ image.code=" /DVIPNGDICT 100 dict def DVIPNGDICT begin /showpage {} def "; image.postcode=" end showpage\n"; /* Use alpha blending, and render transparent postscript images. The alpha blending works correctly only from libgd 2.0.12 upwards */ #ifdef HAVE_GDIMAGEPNGEX if (page_imagep->trueColor) { int tllx=llx,tlly=lly,turx=urx,tury=ury; DEBUG_PRINT((DEBUG_DVI | DEBUG_GS), ("\n GS RENDER \t%s -> pngalpha ",image.filename)); if (!clip) { /* Render across the whole image */ tllx=llx-(hh+1)*72/hresolution; tlly=lly-(gdImageSY(page_imagep)-vv-1)*72/vresolution; turx=llx+(gdImageSX(page_imagep)-hh)*72/hresolution; tury=lly+(vv+1)*72/vresolution; DEBUG_PRINT((DEBUG_DVI | DEBUG_GS), ("\n EXPAND BBOX \t%d %d %d %d -> %d %d %d %d", llx,lly,urx,ury,tllx,tlly,turx,tury)); #ifdef DEBUG } else { DEBUG_PRINT((DEBUG_DVI | DEBUG_GS),(", CLIPPED TO BBOX")); #endif } psimage = ps2png(&image, "-sDEVICE=pngalpha", hresolution, vresolution, tllx, tlly, turx, tury, 255,255,255); if (psimage==NULL) Warning("No GhostScript pngalpha output, opaque image inclusion"); } else Warning("Palette output, opaque image inclusion"); #endif if (psimage==NULL) { /* png256 gives inferior result */ DEBUG_PRINT((DEBUG_DVI | DEBUG_GS), ("\n GS RENDER \t%s -> png16m", image.filename)); psimage = ps2png(&image, "-sDEVICE=png16m", hresolution, vresolution, llx, lly, urx, ury, cstack[0].red,cstack[0].green,cstack[0].blue); clip=true; page_flags |= PAGE_GAVE_WARN; } if (!clip) { /* Rendering across the whole image */ hh=0; vv=gdImageSY(psimage)-1; } } } UnMmapFile(&(image.fmmap)); if (psimage!=NULL) { DEBUG_PRINT(DEBUG_DVI, ("\n GRAPHIC(X|S) INCLUDE \t%s (%d,%d) res %dx%d at (%d,%d)", psname,gdImageSX(psimage),gdImageSY(psimage), hresolution,vresolution,hh,vv)); #ifdef HAVE_GDIMAGECREATETRUECOLOR if (psimage->trueColor && !page_imagep->trueColor) gdImageTrueColorToPalette(psimage,0,256); #endif #ifdef HAVE_GDIMAGEPNGEX gdImageAlphaBlending(page_imagep,1); #else Warning("Using libgd < 2.0.12, opaque image inclusion"); page_flags |= PAGE_GAVE_WARN; #endif gdImageCopy(page_imagep, psimage, hh, vv-gdImageSY(psimage)+1, 0,0, gdImageSX(psimage),gdImageSY(psimage)); #ifdef HAVE_GDIMAGEPNGEX gdImageAlphaBlending(page_imagep,0); #endif gdImageDestroy(psimage); } else { Warning("Unable to load %s, image will be left blank",image.filename); page_flags |= PAGE_GAVE_WARN; } free(image.filename); Message(BE_NONQUIET,">"); } else { /* Don't draw */ page_flags |= PAGE_TRUECOLOR; DEBUG_PRINT(DEBUG_DVI, ("\n GRAPHIC(X|S) INCLUDE \t%s (%d,%d) res %dx%d at (%d,%d)", psname,pngheight,pngwidth, hresolution,vresolution,hh,vv)); min(x_min,hh); min(y_min,vv-pngheight+1); max(x_max,hh+pngwidth); max(y_max,vv+1); } free(buffer); return; } /******************* Raw PostScript ********************/ if (strncmp(special,"!/preview@version(",18)==0) { int length=0; special+=18; while (special[length]!='\0' && special[length]!=')') length++; if (page_imagep==NULL) Message(BE_NONQUIET," (preview-latex version %.*s)",length,special); free(buffer); return; } /* preview-latex' tightpage option */ if (strncmp(special,"!/preview@tightpage",19)==0) { special+=19; SKIPSPACES(special); if (strncmp(special,"true",4)==0) { if (page_imagep==NULL) Message(BE_NONQUIET," (preview-latex tightpage option detected, will use its bounding box)"); dvi->flags |= DVI_PREVIEW_LATEX_TIGHTPAGE; } free(buffer); return; } if (strncmp(special,"!userdict",9)==0 && strstr(special+10,"7{currentfile token not{stop}if 65781.76 div")!=NULL) { if (page_imagep==NULL && ~dvi->flags & DVI_PREVIEW_LATEX_TIGHTPAGE) Message(BE_NONQUIET," (preview-latex <= 0.9.1 tightpage option detected, will use its bounding box)"); dvi->flags |= DVI_PREVIEW_LATEX_TIGHTPAGE; free(buffer); return; } /* preview-latex' dvips bop-hook redefinition */ if (strncmp(special,"!userdict",9)==0 && strstr(special+10,"preview-bop-")!=NULL) { dvi->flags |= DVI_PREVIEW_BOP_HOOK; if (page_imagep==NULL) Message(BE_VERBOSE," (preview-latex beginning-of-page-hook detected)"); free(buffer); return; } if (dvi->flags & DVI_PREVIEW_BOP_HOOK && ~page_flags & PAGE_PREVIEW_BOP && strncmp(special,"ps::",4)==0) { /* Hokay, decode bounding box */ dviunits adj_llx,adj_lly,adj_urx,adj_ury,ht,dp,wd; adj_llx = strtol(special+4,&special,10); adj_lly = strtol(special,&special,10); adj_urx = strtol(special,&special,10); adj_ury = strtol(special,&special,10); ht = strtol(special,&special,10); dp = strtol(special,&special,10); wd = strtol(special,&special,10); page_flags |= PAGE_PREVIEW_BOP; if (wd>0) { x_offset_tightpage = (-adj_llx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor; x_width_tightpage = x_offset_tightpage +(wd+adj_urx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor; } else { x_offset_tightpage = (-wd+adj_urx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor; x_width_tightpage = x_offset_tightpage +(-adj_llx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor; } /* y-offset = height - 1 */ y_offset_tightpage = (((ht>0)?ht:0)+adj_ury+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor-1; y_width_tightpage = y_offset_tightpage+1 +(((dp>0)?dp:0)-adj_lly+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor; free(buffer); return; } if (special[0]=='"' || strncmp(special,"ps:",3)==0) { /* Raw PostScript */ if (option_flags & NO_RAW_PS) { Warning("Raw PostScript rendering disallowed by --norawps" ); page_flags |= PAGE_GAVE_WARN; free(buffer); return; } if (page_imagep != NULL) { /* Draw into image */ static struct pscode *pscodep=NULL; static bool psenvironment=false; bool nextisps; struct pscode *tmp; gdImagePtr psimage=NULL; char *txt; const char *specialend=special+strlen(special); const char *newspecial=NULL; /* hyperref non-rendering PostScript specials. */ if (strcmp(specialend-11,"pdfmark end")==0 || strcmp(specialend-7,"H.A end")==0 || strcmp(specialend-7,"H.B end")==0 || strcmp(specialend-7,"H.L end")==0 || strcmp(specialend-7,"H.R end")==0 || strcmp(specialend-7,"H.S end")==0 || strcmp(specialend-7,"H.V end")==0 || strncmp(special,"ps:SDict begin /product",23)==0) if (pscodep==NULL) { free(buffer); return; } else newspecial=""; /* pgf PostScript specials. */ else if (strcmp(special,"ps:: pgfo")==0) /* pgf page start. The first numbers are generally valid for the bop instruction, and the latter code is to move the origin to the right place. */ newspecial="ps:: 39139632 55387786 1000 600 600 (tikzdefault.dvi) @start 1 0 bop pgfo 0 0 matrix defaultmatrix transform itransform translate"; else if (strcmp(special,"ps:: pgfc")==0) newspecial="ps:: pgfc eop end"; /* Some packages split their raw PostScript code into several specials. Check for those, and concatenate them so that they're given to one and the same invocation of gs */ else if (strncmp(special,"ps::[begin]",11)==0) psenvironment=true; else if (strncmp(special,"ps::[end]",9)==0) psenvironment=false; if (pscodep==NULL) { Message(BE_NONQUIET," next != NULL) tmp=tmp->next; if ((tmp->next=malloc(sizeof(struct pscode)))==NULL) Fatal("cannot malloc space for raw PostScript struct"); tmp=tmp->next; } nextisps=DVIIsNextPSSpecial(dvi); if (psenvironment || nextisps) { if (!nextisps) { Warning("PostScript environment contains DVI commands"); page_flags |= PAGE_GAVE_WARN; } DEBUG_PRINT(DEBUG_GS,("\n PS SPECIAL ")); if (newspecial == NULL) newspecial=special; if ((txt=malloc(strlen(newspecial)+1))==NULL) Fatal("cannot allocate space for raw PostScript special"); strcpy(txt,newspecial); PSCodeInit(tmp,txt); free(buffer); return; } DEBUG_PRINT(DEBUG_DVI,("\n LAST PS SPECIAL ")); if (newspecial != NULL) { if ((special=malloc(strlen(newspecial)+1))==NULL) Fatal("cannot allocate space for raw PostScript special"); strcpy(special,newspecial); } PSCodeInit(tmp,special); /* Now, render image */ if (option_flags & NO_GHOSTSCRIPT) Warning("GhostScript calls disallowed by --nogs" ); else { /* Use alpha blending, and render transparent postscript images. The alpha blending works correctly only from libgd 2.0.12 upwards */ #ifdef HAVE_GDIMAGEPNGEX if (page_imagep->trueColor) { /* Render across the whole image */ psimage = ps2png(pscodep, "-sDEVICE=pngalpha", dpi,dpi, -(hh+1)*72/dpi, -(gdImageSY(page_imagep)-vv-1)*72/dpi, (gdImageSX(page_imagep)-hh)*72/dpi, (vv+1)*72/dpi, 255,255,255); if (psimage!=NULL) { gdImageAlphaBlending(page_imagep,1); gdImageCopy(page_imagep, psimage, 0,0,0,0, gdImageSX(psimage),gdImageSY(psimage)); gdImageAlphaBlending(page_imagep,0); gdImageDestroy(psimage); } else Warning("No image output from inclusion of raw PostScript"); } else Warning("Palette output, cannot include raw PostScript"); #else Warning("Using libgd < 2.0.12, unable to include raw PostScript"); #endif } while(pscodep->next != NULL) { tmp=pscodep->next; free(pscodep->special); free(pscodep); pscodep=tmp; } free(pscodep); pscodep=NULL; if (newspecial != NULL) free(special); if (psimage==NULL) page_flags |= PAGE_GAVE_WARN; Message(BE_NONQUIET,">"); } else { /* Don't draw */ page_flags |= PAGE_TRUECOLOR; } free(buffer); return; } if (strncmp(special,"papersize=",10)==0) { /* papersize spec, ignored */ free(buffer); return; } if (special[0]=='!' || strncmp(special,"header=",7)==0) { /* PS header */ newpsheader(special); free(buffer); return; } if (strncmp(special,"src:",4)==0) { /* source special */ if ( page_imagep != NULL ) Message(BE_NONQUIET," at (%ld,%ld) source \\special{%s}", hh, vv, special); free(buffer); return; } if ( page_imagep != NULL ) { Warning("at (%ld,%ld) unimplemented \\special{%s}", hh, vv, special); page_flags |= PAGE_GAVE_WARN; } free(buffer); }