Back to index

tetex-bin  3.0
special.c
Go to the documentation of this file.
00001 /* special.c */
00002 
00003 /************************************************************************
00004 
00005   Part of the dvipng distribution
00006 
00007   This program is free software; you can redistribute it and/or modify
00008   it under the terms of the GNU General Public License as published by
00009   the Free Software Foundation; either version 2 of the License, or
00010   (at your option) any later version.
00011 
00012   This program is distributed in the hope that it will be useful, but
00013   WITHOUT ANY WARRANTY; without even the implied warranty of
00014   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015   General Public License for more details.
00016 
00017   You should have received a copy of the GNU General Public License
00018   along with this program; if not, write to the Free Software
00019   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
00020   02111-1307, USA.
00021 
00022   Copyright (C) 2002-2005 Jan-Åke Larsson
00023 
00024 ************************************************************************/
00025 
00026 #include "dvipng.h"
00027 #include <fcntl.h>
00028 #if HAVE_ALLOCA_H
00029 # include <alloca.h>
00030 #endif
00031 
00032 gdImagePtr
00033 ps2png(const char *psfile, int hresolution, int vresolution, 
00034        int urx, int ury, int llx, int lly)
00035 {
00036 #ifndef MIKTEX
00037   int downpipe[2], uppipe[2];
00038   pid_t pid;
00039 #else
00040   HANDLE hPngStream;
00041   HANDLE hPsStream;
00042   HANDLE hStdErr;
00043   PROCESS_INFORMATION pi;
00044   _TCHAR szCommandLine[2048];
00045   _TCHAR szGsPath[_MAX_PATH];
00046   int fd;
00047 #endif
00048   FILE *psstream=NULL, *pngstream=NULL;
00049   char resolution[STRSIZE]; 
00050   /*   char devicesize[STRSIZE];  */
00051   /* For some reason, png256 gives inferior result */
00052   char *device="-sDEVICE=png16m";  
00053   gdImagePtr psimage=NULL;
00054   static bool showpage;
00055 
00056   sprintf(resolution, "-r%dx%d",hresolution,vresolution);
00057   /* Future extension for \rotatebox
00058   status=sprintf(devicesize, "-g%dx%d",
00059                //(int)((sin(atan(1.0))+1)*
00060                (urx - llx)*hresolution/72,//), 
00061                //(int)((sin(atan(1.0))+1)*
00062                (ury - lly)*vresolution/72);//);
00063   */
00064   /* png16m being the default, this code is not needed
00065    * #ifdef HAVE_GDIMAGECREATETRUECOLOR
00066    * if (flags & RENDER_TRUECOLOR) 
00067    * device="-sDEVICE=png16m";
00068    * #endif  
00069    */
00070 
00071 #ifndef MIKTEX
00072   if (pipe(downpipe) || pipe(uppipe)) return(NULL);
00073   /* Ready to fork */
00074   pid = fork ();
00075   if (pid == 0) { /* Child process.  Execute gs. */       
00076     close(downpipe[1]);
00077     dup2(downpipe[0], STDIN_FILENO);
00078     close(downpipe[0]);
00079 #ifdef DEBUG
00080     if (flags & NO_GSSAFER) {
00081       DEBUG_PRINT(DEBUG_GS,
00082                 ("\n  GS CALL:\t%s %s %s %s %s %s %s %s %s %s ",/* %s", */
00083                  GS_PATH, device, resolution, /* devicesize, */
00084                  "-dBATCH", "-dNOPAUSE", "-q", 
00085                  "-sOutputFile=-", 
00086                  "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00087                  "-"));
00088     } else {
00089       DEBUG_PRINT(DEBUG_GS,
00090                 ("\n  GS CALL:\t%s %s %s %s %s %s %s %s %s %s %s ",/* %s", */
00091                  GS_PATH, device, resolution, /*devicesize,*/
00092                  "-dBATCH", "-dNOPAUSE", "-dSAFER", "-q", 
00093                  "-sOutputFile=-", 
00094                  "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00095                  "-"));
00096     }
00097 #endif
00098     close(uppipe[0]);
00099     dup2(uppipe[1], STDOUT_FILENO);
00100     close(uppipe[1]);
00101     if (flags & NO_GSSAFER) 
00102       execl (GS_PATH, GS_PATH, device, resolution, /*devicesize,*/
00103             "-dBATCH", "-dNOPAUSE", "-q", 
00104             "-sOutputFile=-", 
00105             "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00106             "-",NULL);
00107     else
00108       execl (GS_PATH, GS_PATH, device, resolution, /*devicesize,*/
00109             "-dBATCH", "-dNOPAUSE", "-dSAFER", "-q", 
00110             "-sOutputFile=-", 
00111             "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00112             "-",NULL);
00113     _exit (EXIT_FAILURE);
00114   }
00115   /* Parent process. */
00116   
00117   close(downpipe[0]);
00118   psstream=fdopen(downpipe[1],"wb");
00119   if (psstream == NULL) 
00120     close(downpipe[1]);
00121   close(uppipe[1]);
00122   pngstream=fdopen(uppipe[0],"rb");
00123   if (pngstream == NULL) 
00124     close(uppipe[0]);
00125 #else /* MIKTEX */
00126   if (! miktex_find_miktex_executable("mgs.exe", szGsPath)) {
00127       Warning("Ghostscript could not be found");
00128       return(NULL);
00129   }
00130   if (flags & NO_GSSAFER) {
00131     DEBUG_PRINT(DEBUG_GS,
00132               ("\n  GS CALL:\t%s %s %s %s %s %s %s %s %s %s ",/* %s",*/
00133                szGsPath, device, resolution, /*devicesize,*/
00134                "-dBATCH", "-dNOPAUSE", "-q", 
00135                "-sOutputFile=-", 
00136                "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00137                "-"));
00138     sprintf(szCommandLine,"\"%s\" %s %s %s %s %s %s %s %s %s",/* %s",*/
00139            szGsPath, device, resolution, /*devicesize,*/
00140            "-dBATCH", "-dNOPAUSE", "-q", 
00141            "-sOutputFile=-", 
00142            "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00143            "-");
00144   } else {
00145     DEBUG_PRINT(DEBUG_GS,
00146               ("\n  GS CALL:\t%s %s %s %s %s %s %s %s %s %s %s ",/* %s",*/
00147                szGsPath, device, resolution, /*devicesize,*/
00148                "-dBATCH", "-dNOPAUSE", "-dSAFER", "-q", 
00149                "-sOutputFile=-", 
00150                "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00151                "-"));
00152     sprintf(szCommandLine,"\"%s\" %s %s %s %s %s %s %s %s %s %s",/* %s",*/
00153            szGsPath, device, resolution, /*devicesize,*/
00154            "-dBATCH", "-dNOPAUSE", "-dSAFER", "-q", 
00155            "-sOutputFile=-", 
00156            "-dTextAlphaBits=4", "-dGraphicsAlphaBits=4",
00157            "-");
00158   }
00159   if (! miktex_start_process_3(szCommandLine, &pi, INVALID_HANDLE_VALUE,
00160                             &hPsStream, &hPngStream, &hStdErr, 0)) {
00161       Warning("Ghostscript could not be started");
00162       return(NULL);
00163   }
00164   CloseHandle (pi.hThread);
00165   fd = _open_osfhandle((intptr_t)hPsStream, _O_WRONLY);
00166   if (fd >= 0) { 
00167     psstream = _tfdopen(fd, "wb");
00168     if (psstream == NULL) 
00169       _close (fd);
00170   }
00171   fd = _open_osfhandle((intptr_t)hPngStream, _O_RDONLY);
00172   if (fd >= 0) {
00173     pngstream = _tfdopen(fd, "rb");
00174     if (pngstream == NULL) 
00175       _close (fd);
00176   }
00177 #endif 
00178   if (psstream) {
00179     DEBUG_PRINT(DEBUG_GS,("\n  PS CODE:\t<</PageSize[%d %d]/PageOffset[%d %d[1 1 dtransform exch]{0 ge{neg}if exch}forall]>>setpagedevice",
00180                        urx - llx, ury - lly,llx,lly));
00181     fprintf(psstream, "<</PageSize[%d %d]/PageOffset[%d %d[1 1 dtransform exch]{0 ge{neg}if exch}forall]>>setpagedevice\n",
00182            urx - llx, ury - lly,llx,lly);
00183     if ( cstack[0].red < 255 || cstack[0].green < 255 || cstack[0].blue < 255 ) {
00184       DEBUG_PRINT(DEBUG_GS,("\n  PS CODE:\tgsave %f %f %f setrgbcolor clippath fill grestore",
00185                          cstack[0].red/255.0, cstack[0].green/255.0, cstack[0].blue/255.0));
00186       fprintf(psstream, "gsave %f %f %f setrgbcolor clippath fill grestore",
00187              cstack[0].red/255.0, cstack[0].green/255.0, cstack[0].blue/255.0);
00188     }
00189     DEBUG_PRINT(DEBUG_GS,("\n  PS CODE:\t(%s) run", psfile));
00190     fprintf(psstream, "(%s) run\n", psfile);
00191     if (showpage) {
00192       DEBUG_PRINT(DEBUG_GS,("\n  PS CODE:\tshowpage"));
00193       fprintf(psstream, "showpage\n");
00194     }
00195     DEBUG_PRINT(DEBUG_GS,("\n  PS CODE:\tquit"));
00196     fprintf(psstream, "quit\n");
00197     fclose(psstream);
00198   }
00199   if (pngstream) {
00200     psimage = gdImageCreateFromPng(pngstream);
00201     fclose(pngstream);
00202   }
00203 #ifdef MIKTEX
00204   CloseHandle(pi.hProcess);
00205 #endif
00206 #ifdef HAVE_GDIMAGETRUECOLORTOPALETTE
00207 #ifdef HAVE_GDIMAGECREATETRUECOLOR
00208   if (!flags & RENDER_TRUECOLOR)
00209 #endif
00210     gdImageTrueColorToPalette(psimage,0,256);
00211 #endif
00212   if (psimage == NULL) {
00213     DEBUG_PRINT(DEBUG_GS,("\n  GS OUTPUT:\tNO IMAGE "));
00214     if (!showpage) {
00215       showpage=true;
00216       DEBUG_PRINT(DEBUG_GS,("(will try adding \"showpage\") "));
00217       psimage=ps2png(psfile, hresolution, vresolution, urx, ury, llx, lly);
00218       showpage=false;
00219     }
00220 #ifdef DEBUG
00221   } else {
00222     DEBUG_PRINT(DEBUG_GS,("\n  GS OUTPUT:\t%dx%d image ",
00223                        gdImageSX(psimage),gdImageSY(psimage)));
00224 #endif
00225   }
00226   return psimage;
00227 }
00228 
00229 /*-->SetSpecial*/
00230 /*********************************************************************/
00231 /****************************  SetSpecial  ***************************/
00232 /*********************************************************************/
00233 
00234 void SetSpecial(char * special, int32_t length, int32_t hh, int32_t vv)
00235 /* interpret a \special command, made up of keyword=value pairs */
00236 /* Color specials only for now. Warn otherwise. */
00237 {
00238   char *buffer, *token;
00239 
00240   DEBUG_PRINT(DEBUG_DVI,(" '%.*s'",length,special));
00241 
00242   buffer = alloca(sizeof(char)*(length+1));
00243   if (buffer==NULL) 
00244     Fatal("Cannot allocate space for special string");
00245 
00246   strncpy(buffer,special,length);
00247   buffer[length]='\0';
00248 
00249   token = strtok(buffer," ");
00250   /********************** Color specials ***********************/
00251   if (strcmp(token,"background")==0) {
00252     token = strtok(NULL,"\0");
00253     background(token);
00254     return;
00255   }
00256   if (strcmp(token,"color")==0) {
00257     token = strtok(NULL,"\0");
00258     if (strncmp(token,"push",4)==0) {
00259       token = strtok(token," ");
00260       token = strtok(NULL,"\0");
00261       pushcolor(token);
00262     } else {
00263       if (strncmp(token,"pop",3)==0)
00264        popcolor();
00265       else 
00266        resetcolorstack(token);
00267     }
00268     return;
00269   }
00270 
00271   /******************* Postscript inclusion ********************/
00272   if (strncmp(token,"PSfile=",7)==0) { /* PSfile */
00273     char* psname = token+7;
00274     int llx=0,lly=0,urx=0,ury=0,rwi=0,rhi=0;
00275     int hresolution,vresolution;
00276 
00277     /* Remove quotation marks around filename */
00278     if (*psname=='"') {
00279       char* tmp;
00280       psname++;
00281       tmp=strrchr(psname,'"');
00282       if (tmp!=NULL) *tmp='\0';
00283     }
00284 
00285     while((token = strtok(NULL," ")) != NULL) {
00286       if (strncmp(token,"llx=",4)==0) llx = atoi(token+4);
00287       if (strncmp(token,"lly=",4)==0) lly = atoi(token+4);
00288       if (strncmp(token,"urx=",4)==0) urx = atoi(token+4);
00289       if (strncmp(token,"ury=",4)==0) ury = atoi(token+4);
00290       if (strncmp(token,"rwi=",4)==0) rwi = atoi(token+4);
00291       if (strncmp(token,"rhi=",4)==0) rhi = atoi(token+4);
00292     }
00293     
00294     /* Calculate resolution, and use our base resolution as a fallback. */
00295     /* The factor 10 is magic, the dvips graphicx driver needs this.    */
00296     hresolution = dpi*rwi/(urx - llx)/10;
00297     vresolution = dpi*rhi/(ury - lly)/10;
00298     if (vresolution==0) vresolution = hresolution;
00299     if (hresolution==0) hresolution = vresolution;
00300     if (hresolution==0) hresolution = vresolution = dpi;
00301     
00302     if (page_imagep != NULL) { /* Draw into image */
00303       char* psfile;
00304       gdImagePtr psimage=NULL;
00305 
00306       /*---------- Cache ----------*/
00307       char* cachename = NULL;
00308       gdImagePtr cacheimage=NULL;
00309 
00310       TEMPSTR(psfile,kpse_find_file(psname,kpse_pict_format,0));
00311       if (flags & CACHE_IMAGES) { /* Find cached image, if it exists */
00312        char *cachefile,*separator;
00313 
00314        cachename = alloca(sizeof(char)*(strlen(psname+5)));
00315        if (cachename==NULL) 
00316          Fatal("Cannot allocate space for cached image filename");
00317        strcpy(cachename,psname);
00318        separator = strrchr(cachename,'.');
00319        if (separator!=NULL)
00320          *separator='\0';
00321        strcat(cachename,".png");
00322        cachefile = kpse_find_file(cachename,kpse_pict_format,0);
00323        if (cachefile!=NULL) {
00324          FILE* cachefilep = fopen(cachefile,"rb");
00325 
00326          if (cachefilep!=NULL) {
00327            cacheimage = gdImageCreateFromPng(cachefilep);
00328            fclose(cachefilep);
00329          }
00330          free(cachefile);
00331        }
00332        psimage = cacheimage;
00333       }
00334       /*---------- End Cache ----------*/
00335       Message(BE_NONQUIET,"<%s",psname);
00336       if (psimage==NULL) {
00337        /* No cached image, convert postscript */
00338        if (psfile == NULL) {
00339          Warning("PS file %s not found, image will be left blank", psname );
00340          flags |= PAGE_GAVE_WARN;
00341        } else if (flags & NO_GHOSTSCRIPT) {
00342            Warning("GhostScript calls disallowed by --noghostscript", 
00343                   psfile );
00344            flags |= PAGE_GAVE_WARN;
00345        } else {
00346          psimage = ps2png(psfile, hresolution, vresolution, 
00347                         urx, ury, llx, lly);
00348          if ( psimage == NULL ) {
00349            Warning("Unable to convert %s to PNG, image will be left blank", 
00350                   psfile );
00351            flags |= PAGE_GAVE_WARN;
00352          }
00353        }
00354       }
00355       /*---------- Store Cache ----------*/
00356       if (flags & CACHE_IMAGES && cachename !=NULL && 
00357          cacheimage==NULL && psimage != NULL) {
00358        /* Cache image not found, save converted postscript */
00359        FILE* cachefilep = fopen(cachename,"wb");
00360        if (cachefilep!=NULL) {
00361          gdImagePng(psimage,cachefilep);
00362          fclose(cachefilep);
00363        } else
00364          Warning("Unable to cache %s as PNG", psfile );
00365       } 
00366       /*---------- End Store Cache ----------*/
00367       if (psimage!=NULL) {
00368        DEBUG_PRINT(DEBUG_DVI,
00369                   ("\n  PS-PNG INCLUDE \t%s (%d,%d) res %dx%d at (%d,%d)",
00370                    psfile,
00371                    gdImageSX(psimage),gdImageSY(psimage),
00372                    hresolution,vresolution,
00373                    hh, vv));
00374        gdImageCopy(page_imagep, psimage, 
00375                   hh, vv-gdImageSY(psimage),
00376                   0,0,
00377                   gdImageSX(psimage),gdImageSY(psimage));
00378        gdImageDestroy(psimage);
00379       }
00380       Message(BE_NONQUIET,">");
00381     } else { /* Don't draw */
00382       int pngheight,pngwidth;
00383       
00384       /* Convert from postscript 72 dpi resolution to our given resolution */
00385       pngheight = (vresolution*(ury - lly)+71)/72; /* +71: do 'ceil' */
00386       pngwidth  = (hresolution*(urx - llx)+71)/72;
00387       DEBUG_PRINT(DEBUG_DVI,("\n  PS-PNG INCLUDE \t(%d,%d)", 
00388                  pngwidth,pngheight));
00389       min(x_min,hh);
00390       min(y_min,vv-pngheight);
00391       max(x_max,hh+pngwidth);
00392       max(y_max,vv);
00393     }
00394     return;
00395   }
00396 
00397   /* preview-latex' tightpage option */
00398   if (strcmp(buffer,"!userdict")==0 
00399       && strcmp(buffer+10,"begin/bop-hook{7{currentfile token not{stop}if 65781.76 div DVImag mul}repeat 72 add 72 2 copy gt{exch}if 4 2 roll neg 2 copy lt{exch}if dup 0 gt{pop 0 exch}{exch dup 0 lt{pop 0}if}ifelse 720 add exch 720 add 3 1 roll 4{5 -1 roll add 4 1 roll}repeat <</PageSize[5 -1 roll 6 index sub 5 -1 roll 5 index sub]/PageOffset[7 -2 roll [1 1 dtransform exch]{0 ge{neg}if exch}forall]>>setpagedevice//bop-hook exec}bind def end")==0) {
00400     if (page_imagep==NULL) 
00401       Message(BE_NONQUIET,"preview-latex's tightpage option detected, will use its bounding box.\n");
00402     flags |= PREVIEW_LATEX_TIGHTPAGE;
00403     return;
00404   }
00405   if (strncmp(token,"ps::",4)==0) {
00406     /* Hokay, decode bounding box */
00407     dviunits adj_llx,adj_lly,adj_urx,adj_ury,ht,dp,wd;
00408     adj_llx = atoi(token+4);
00409     token = strtok(NULL," ");
00410     adj_lly = atoi(token);
00411     token = strtok(NULL," ");
00412     adj_urx = atoi(token);
00413     token = strtok(NULL," ");
00414     adj_ury = atoi(token);
00415     token = strtok(NULL," ");
00416     ht = atoi(token);
00417     token = strtok(NULL," ");
00418     dp = atoi(token);
00419     token = strtok(NULL," ");
00420     wd = atoi(token);
00421     if (wd>0) {
00422       x_offset_tightpage = 
00423        (-adj_llx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor;
00424       x_width_tightpage  = x_offset_tightpage
00425        +(wd+adj_urx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor;
00426     } else {
00427       x_offset_tightpage = 
00428        (-wd+adj_urx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor;
00429       x_width_tightpage  = x_offset_tightpage
00430        +(-adj_llx+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor;
00431     }
00432     /* y-offset = height - 1 */
00433     y_offset_tightpage = 
00434       (((ht>0)?ht:0)+adj_ury+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor-1;
00435     y_width_tightpage  = y_offset_tightpage+1
00436       +(((dp>0)?dp:0)-adj_lly+dvi->conv*shrinkfactor-1)/dvi->conv/shrinkfactor;
00437     return;
00438   }
00439 
00440   if (strncmp(token,"header=",7)==0 || token[0]=='!') { /* header, ignored */
00441     if ( page_imagep != NULL )
00442       Warning("at (%ld,%ld) ignored header \\special{%.*s}.",
00443              hh, vv, length,special);
00444     return;
00445   }
00446   if (strncmp(token,"src:",4)==0) { /* source special */
00447     if ( page_imagep != NULL )
00448       Message(BE_NONQUIET," at (%ld,%ld) source \\special{%.*s}",
00449              hh, vv, length,special);
00450     return;
00451   }
00452   if ( page_imagep != NULL || flags & MODE_PICKY ) {
00453     Warning("at (%ld,%ld) unimplemented \\special{%.*s}.",
00454            hh, vv, length,special);
00455     flags |= PAGE_GAVE_WARN;
00456   }
00457 }