GraphicsPostscript.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. /* GraphicsPostscript.cpp
  2. *
  3. * Copyright (C) 1992-2011,2014,2015,2016,2017 Paul Boersma
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include <math.h> /* For 'floor' and 'ceil' in BoundingBox. */
  19. #include <time.h> /* For creation date. */
  20. #include "GraphicsP.h"
  21. #include "Printer.h"
  22. Thing_implement (GraphicsPostscript, Graphics, 0);
  23. static void downloadPrologAndSetUp (GraphicsPostscript me) {
  24. /*
  25. * Procedures section of the document prolog: procedure definitions valid for all pages.
  26. */
  27. my d_printf (my d_file, "%%%%BeginProlog\n");
  28. my d_printf (my d_file, "%%%%BeginResource: procset (ppgb GraphicsPostscript procs) 1.0 0\n");
  29. my d_printf (my d_file, "/N { newpath } bind def /M { newpath moveto } bind def /L { rlineto } bind def\n");
  30. my d_printf (my d_file, "/F { 10 { 1 exch rlineto } repeat } bind def\n");
  31. my d_printf (my d_file, "/C { 0 360 arc stroke } bind def /FC { 0 360 arc fill } bind def\n");
  32. my d_printf (my d_file,
  33. "/PraatEncoding [\n"
  34. " /dotlessi/Aogonek/aogonek/Cacute/cacute/Ccaron/ccaron/Dcaron/dcaron/Dbar\n"
  35. " /dbar/Ecaron/ecaron/Eogonek/eogonek/Gcaron/gcaron/Lslash/lslash/Nacute\n"
  36. " /nacute/Ncaron/ncaron/Ohungarumlaut/ohungarumlaut/Rcaron/rcaron/Sacute/sacute/Scaron\n"
  37. " /scaron/Tcaron/space/exclam/quotedbl/numbersign/dollar/percent/ampersand/quotesinglright\n"
  38. " /parenleft/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one\n"
  39. " /two/three/four/five/six/seven/eight/nine/colon/semicolon\n"
  40. " /less/equal/greater/question/at/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z\n"
  41. " /bracketleft/backslash/bracketright/asciicircum/underscore/quotesinglleft\n"
  42. " /a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/braceleft/bar/braceright/asciitilde\n"
  43. " /Zdot/Adieresis/Aring/Ccedilla/Eacute/Ntilde/Odieresis/Udieresis/aacute/agrave/acircumflex\n"
  44. " /adieresis/atilde/aring/ccedilla/eacute/egrave/ecircumflex/edieresis/iacute/igrave/icircumflex\n");
  45. my d_printf (my d_file,
  46. " /idieresis/ntilde/oacute/ograve/ocircumflex/odieresis/otilde/uacute/ugrave/ucircumflex/udieresis\n"
  47. " /dagger/degree/cent/sterling/section/bullet/paragraph/germandbls/registered/copyright/trademark\n"
  48. " /acute/dieresis/notequal/AE/Oslash/infinity/plusminus/lessequal/greaterequal/yen/mu\n"
  49. " /partialdiff/summation/product/pi/integral/ordfeminine/ordmasculine/Omega/ae/oslash\n"
  50. " /questiondown/exclamdown/logicalnot/radical/florin/approxequal/Delta/guillemotleft/guillemotright\n"
  51. " /ellipsis/zdot/Agrave/Atilde/Otilde/OE/oe/endash/emdash/quotedblleft/quotedblright\n"
  52. " /quoteleft/quoteright/divide/lozenge/ydieresis/Ydieresis/fraction/currency\n"
  53. " /guilsinglleft/guilsinglright/fi/fl/daggerdbl/periodcentered/quotesinglbase/quotedblbase\n"
  54. " /perthousand/Acircumflex/Ecircumflex/Aacute/Edieresis/Egrave/Iacute/Icircumflex/Idieresis\n"
  55. " /Igrave/Oacute/Ocircumflex/apple/Ograve/Uacute/Ucircumflex/Ugrave/tcaron\n"
  56. " /Uhungarumlaut/uhungarumlaut/Uring/uring/Yacute/yacute/Zacute/zacute/Zcaron/zcaron ] def\n");
  57. my d_printf (my d_file,
  58. "/PraatEncode { /font exch def /base exch def\n"
  59. " /basedict base findfont def /new basedict maxlength dict def\n"
  60. " basedict { exch dup dup /FID ne exch /Encoding ne and\n"
  61. " { exch new 3 1 roll put } { pop pop } ifelse } forall\n"
  62. " new /FontName font put new /Encoding PraatEncoding put font new definefont pop } def\n");
  63. my d_printf (my d_file, "%%%%EndResource\n");
  64. my d_printf (my d_file, "%%%%EndProlog\n");
  65. /*
  66. * Document setup: graphics state changes persistent across showpage calls.
  67. */
  68. my d_printf (my d_file, "%%%%BeginSetup\n");
  69. //my d_printf (my d_file, "%d %d { dup mul exch dup mul add 1.0 exch sub } setscreen\n", my spotsDensity, my spotsAngle);
  70. my d_printf (my d_file, "true setstrokeadjust\n");
  71. my d_printf (my d_file, "%%%%EndSetup\n");
  72. }
  73. static void initPage (GraphicsPostscript me) {
  74. ++ my pageNumber;
  75. if (my printer && my pageNumber > 1) downloadPrologAndSetUp (me); // has to be repeated for every page
  76. if (my job) {
  77. my d_printf (my d_file, "%%%%Page: %d %d\n", my pageNumber, my pageNumber);
  78. /*my d_printf (my d_file, "save\n");*/
  79. my d_printf (my d_file, "%%%%BeginPageSetup\n");
  80. }
  81. my d_printf (my d_file, "%g setlinewidth 2 setlinejoin\n", my resolution / 192.0); /* 0.375 point */
  82. if (my job || my printer) {
  83. if (my landscape)
  84. my d_printf (my d_file, "%d 0 translate 90 rotate ", (int) (my paperHeight * 72 * my magnification));
  85. }
  86. my d_printf (my d_file, "%.6g dup scale\n", 72.0 * my magnification / my resolution);
  87. if (my job) my d_printf (my d_file, "%%%%EndPageSetup\n");
  88. my lastFid = nullptr;
  89. }
  90. static void exitPage (GraphicsPostscript me) {
  91. int font, style;
  92. /*
  93. * A showpage is only needed if we are printing to a file.
  94. * If we are printing directly to a printer, the page will be ejected
  95. * by EndPage (Windows NT/2000/XP/Vista) or PMSessionEndPage (MacOSX).
  96. */
  97. if (my d_file) {
  98. if (my job) {
  99. /*my d_printf (my d_file, "restore\n");*/
  100. my d_printf (my d_file, "showpage\n");
  101. } else if (my eps) {
  102. my d_printf (my d_file, "showpage %% redefined by encapsulating program\n");
  103. }
  104. }
  105. for (font = 0; font <= kGraphics_font_DINGBATS; font ++)
  106. for (style = 0; style <= Graphics_BOLD_ITALIC; style ++)
  107. Melder_free (my fontInfos [font] [style]);
  108. my loadedXipa = false; // FIXME: include this because of the unpredictable page order with DSC?
  109. }
  110. void structGraphicsPostscript :: v_destroy () noexcept {
  111. exitPage (this);
  112. if (our d_file) {
  113. if (our job) {
  114. our d_printf (our d_file, "%%%%Trailer\n");
  115. our d_printf (our d_file, "%%%%Pages: %d\n", our pageNumber);
  116. }
  117. our d_printf (our d_file, "%%%%EOF\n"); // BUG. Correct according to DSC. But not good in EPS files?
  118. fclose (our d_file);
  119. }
  120. GraphicsPostscript_Parent :: v_destroy ();
  121. }
  122. autoGraphics Graphics_create_postscriptjob (MelderFile file, int resolution, kGraphicsPostscript_spots spots,
  123. kGraphicsPostscript_paperSize paperSize, kGraphicsPostscript_orientation rotation, double magnification)
  124. {
  125. autoGraphicsPostscript me = Thing_new (GraphicsPostscript);
  126. time_t today;
  127. my postScript = true, my yIsZeroAtTheTop = false, my languageLevel = 2;
  128. my job = true, my eps = false, my printer = false;
  129. my d_printf = (int (*)(void *, const char*, ...)) fprintf;
  130. Graphics_init (me.get(), resolution); // virtual resolution; may differ from that of the printer; OK if always 600 dpi
  131. my photocopyable = spots == kGraphicsPostscript_spots::PHOTOCOPYABLE;
  132. if (my photocopyable) { my spotsDensity = 85; my spotsAngle = 35; }
  133. else { my spotsDensity = 106; my spotsAngle = 46; }
  134. if (paperSize == kGraphicsPostscript_paperSize::A3) my paperWidth = 842 / 72.0, my paperHeight = 1191 / 72.0;
  135. else if (paperSize == kGraphicsPostscript_paperSize::US_LETTER) my paperWidth = 612 / 72.0, my paperHeight = 792 / 72.0;
  136. else my paperWidth = 595 / 72.0, my paperHeight = 842 / 72.0;
  137. my landscape = rotation == kGraphicsPostscript_orientation::LANDSCAPE;
  138. my magnification = magnification;
  139. my includeFonts = true;
  140. my d_file = Melder_fopen (file, "w");
  141. /*
  142. * The Device Coordinates are the PostScript user coordinates.
  143. * They are chosen in such a way that a distance of 1 in device coordinates
  144. * equals one dot if the printer's resolution is 'resolution' dots per inch.
  145. * Take a sensible default margin: half an inch on all sides.
  146. */
  147. my d_x1DC = my d_x1DCmin = resolution / 2;
  148. my d_x2DC = my d_x2DCmax = (my paperWidth - 0.5) * resolution;
  149. my d_y1DC = my d_y1DCmin = resolution / 2;
  150. my d_y2DC = my d_y2DCmax = (my paperHeight - 0.5) * resolution;
  151. /*
  152. * Now don't just set x1wNDC etc, but force computation of the scaling as well.
  153. */
  154. Graphics_setWsWindow (me.get(), 0, my paperWidth - 1.0, 13.0 - my paperHeight, 12.0);
  155. /*
  156. * We will adhere to version 3.0 of the Document Structuring Conventions for print jobs.
  157. */
  158. my d_printf (my d_file, "%%!PS-Adobe-3.0\n");
  159. my d_printf (my d_file, "%%%%Creator: Praat Shell 4.2\n");
  160. my d_printf (my d_file, "%%%%Title: %s\n", Melder_peek32to8 (MelderFile_name (file)));
  161. today = time (nullptr);
  162. my d_printf (my d_file, "%%%%CreationDate: %s", ctime (& today)); // contains newline symbol
  163. my d_printf (my d_file, "%%%%PageOrder: Special\n");
  164. my d_printf (my d_file, "%%%%Pages: (atend)\n");
  165. my d_printf (my d_file, "%%%%EndComments\n");
  166. downloadPrologAndSetUp (me.get());
  167. initPage (me.get());
  168. return me.move();
  169. }
  170. #if defined (macintosh)
  171. static int Eps_postScript_printf (void *stream, const char *format, ... ) {
  172. static char theLine [3002];
  173. char *p;
  174. va_list args;
  175. va_start (args, format);
  176. vsprintf (theLine, format, args);
  177. va_end (args);
  178. for (p = theLine; *p != '\0'; p ++) if (*p == '\n') *p = '\r';
  179. return fwrite (theLine, sizeof (char), strlen (theLine), reinterpret_cast <FILE *> (stream));
  180. }
  181. #endif
  182. autoGraphics Graphics_create_epsfile (MelderFile file, int resolution, enum kGraphicsPostscript_spots spots,
  183. double x1inches, double x2inches, double y1inches, double y2inches, bool includeFonts, bool useSilipaPS)
  184. {
  185. autoGraphicsPostscript me = Thing_new (GraphicsPostscript);
  186. time_t today;
  187. integer left, right, top, bottom;
  188. my postScript = true, my languageLevel = 2;
  189. my job = false, my eps = true, my printer = false;
  190. #if defined (macintosh)
  191. /* Replace newlines with carriage returns to be compatible with MS Word 5.1. */
  192. my d_printf = Eps_postScript_printf;
  193. #else
  194. my d_printf = (int (*)(void *, const char*, ...)) fprintf;
  195. #endif
  196. Graphics_init (me.get(), resolution); // virtual resolution; may differ from that of the printer; OK if always 600 dpi
  197. my photocopyable = spots == kGraphicsPostscript_spots::PHOTOCOPYABLE;
  198. if (my photocopyable) { my spotsDensity = 85; my spotsAngle = 35; }
  199. else { my spotsDensity = 106; my spotsAngle = 46; }
  200. my paperWidth = 7.5, my paperHeight = 11.0;
  201. my landscape = false;
  202. my magnification = 1.0;
  203. my includeFonts = includeFonts;
  204. my useSilipaPS = useSilipaPS;
  205. my d_file = Melder_fopen (file, "w");
  206. my d_x1DC = my d_x1DCmin = 0;
  207. my d_x2DC = my d_x2DCmax = my paperWidth * resolution; // 600 dpi -> 4500 virtual dots
  208. my d_y1DC = my d_y1DCmin = 0;
  209. my d_y2DC = my d_y2DCmax = my paperHeight * resolution; // 600 dpi -> 6600 virtual dots
  210. Graphics_setWsWindow (me.get(), 0, my paperWidth, 12.0 - my paperHeight, 12.0); // force scaling
  211. /*
  212. * We will honour version 3.0 of the DSC for Encapsulated PostScript files,
  213. * which includes supplying the bounding box information.
  214. */
  215. left = Melder_ifloor (x1inches * 72);
  216. right = Melder_iceiling (x2inches * 72);
  217. top = Melder_iceiling ((y2inches - my d_y1wNDC) * 72);
  218. bottom = Melder_ifloor ((y1inches - my d_y1wNDC) * 72);
  219. my d_printf (my d_file, "%%!PS-Adobe-3.0 EPSF-3.0\n");
  220. my d_printf (my d_file, "%%%%BoundingBox: %d %d %d %d\n", left, bottom, right, top);
  221. my d_printf (my d_file, "%%%%Creator: Praat Shell 5.1\n");
  222. /*
  223. * In an EPS file without screen preview, the file name will be visible anyway.
  224. * This leaves us room to show a warning that should keep users from thinking anything is wrong.
  225. */
  226. my d_printf (my d_file, "%%%%Title: NO SCREEN PREVIEW, BUT WILL PRINT CORRECTLY\n");
  227. today = time (nullptr);
  228. my d_printf (my d_file, "%%%%CreationDate: %s", ctime (& today)); /* Contains newline symbol. */
  229. my d_printf (my d_file, "%%%%EndComments\n");
  230. downloadPrologAndSetUp (me.get());
  231. initPage (me.get());
  232. return me.move();
  233. }
  234. #if defined (_WIN32)
  235. autoGraphics Graphics_create_postscriptprinter () {
  236. autoGraphicsPostscript me = Thing_new (GraphicsPostscript);
  237. my postScript = true, my languageLevel = 2;
  238. my job = false, my eps = false, my printer = true;
  239. my d_printf = Printer_postScript_printf;
  240. Graphics_init (me.get(), thePrinter. resolution); // virtual resolution
  241. my photocopyable = thePrinter. spots == kGraphicsPostscript_spots::PHOTOCOPYABLE;
  242. if (my photocopyable) { my spotsDensity = 85; my spotsAngle = 35; }
  243. else { my spotsDensity = 106; my spotsAngle = 46; }
  244. my paperWidth = (double) thePrinter. paperWidth / my resolution;
  245. my paperHeight = (double) thePrinter. paperHeight / my resolution;
  246. my landscape = thePrinter. orientation == kGraphicsPostscript_orientation::LANDSCAPE;
  247. my magnification = thePrinter. magnification;
  248. my includeFonts = true;
  249. my d_x1DC = my d_x1DCmin = my resolution / 2;
  250. my d_x2DC = my d_x2DCmax = (my paperWidth - 0.5) * my resolution;
  251. my d_y1DC = my d_y1DCmin = my resolution / 2;
  252. my d_y2DC = my d_y2DCmax = (my paperHeight - 0.5) * my resolution;
  253. Graphics_setWsWindow (me.get(), 0, my paperWidth - 1.0, 13.0 - my paperHeight, 12.0);
  254. downloadPrologAndSetUp (me.get());
  255. initPage (me.get());
  256. return me.move();
  257. }
  258. #endif
  259. void Graphics_nextSheetOfPaper (Graphics graphics) {
  260. if (graphics -> postScript) {
  261. GraphicsPostscript me = static_cast <GraphicsPostscript> (graphics);
  262. exitPage (me);
  263. if (my printer) Printer_nextPage ();
  264. initPage (me);
  265. } else if (graphics -> printer) {
  266. Printer_nextPage ();
  267. }
  268. }
  269. /* End of file GraphicsPostscript.cpp */