Printer.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /* Printer.cpp
  2. *
  3. * Copyright (C) 1998-2011,2012,2013,2014,2015,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 <unistd.h> // close
  19. #include "melder.h"
  20. #if defined (_WIN32)
  21. #include <windows.h>
  22. #endif
  23. #include "Printer.h"
  24. #include "praat.h" // topShell
  25. #include "Ui.h"
  26. #include "site.h"
  27. #include "GraphicsP.h"
  28. #if cocoa
  29. #include "Picture.h"
  30. #endif
  31. /*
  32. * Everything must look the same on every printer, including on PDF,
  33. * so the margins must be constant with respect to the paper, not to the writable page.
  34. */
  35. /* exported */ struct Printer thePrinter = {
  36. kGraphicsPostscript_spots::DEFAULT, kGraphicsPostscript_paperSize::DEFAULT, kGraphicsPostscript_orientation::DEFAULT, false,
  37. true, kGraphicsPostscript_fontChoiceStrategy::DEFAULT,
  38. 600, 5100, 6600,
  39. 1.0
  40. };
  41. void Printer_prefs () {
  42. Preferences_addEnum (U"Printer.spots", & thePrinter. spots, kGraphicsPostscript_spots, kGraphicsPostscript_spots::DEFAULT);
  43. Preferences_addEnum (U"Printer.paperSize", & thePrinter. paperSize, kGraphicsPostscript_paperSize, kGraphicsPostscript_paperSize::DEFAULT);
  44. Preferences_addBool (U"Printer.allowDirectPostScript", & thePrinter. allowDirectPostScript, true);
  45. Preferences_addEnum (U"Printer.fontChoiceStrategy", & thePrinter. fontChoiceStrategy, kGraphicsPostscript_fontChoiceStrategy, kGraphicsPostscript_fontChoiceStrategy::DEFAULT);
  46. }
  47. #if cocoa
  48. static NSView *theMacView;
  49. #endif
  50. #ifdef _WIN32
  51. static PRINTDLG theWinPrint;
  52. static HDC theWinDC;
  53. #endif
  54. #if defined (_WIN32)
  55. int Printer_postScript_printf (void *stream, const char *format, ... ) {
  56. static union { char chars [3002]; short shorts [1501]; } theLine;
  57. int length;
  58. va_list args;
  59. va_start (args, format);
  60. (void) stream;
  61. vsprintf (theLine.chars + 2, format, args);
  62. length = strlen (theLine.chars + 2);
  63. theLine.shorts [0] = length;
  64. if (length > 0 && theLine.chars [length + 1] == '\n') {
  65. theLine.chars [length + 1] = '\r';
  66. theLine.chars [length + 2] = '\n';
  67. theLine.chars [length + 3] = '\0';
  68. length ++;
  69. }
  70. Escape (theWinDC, POSTSCRIPT_PASSTHROUGH, length + 2, theLine.chars, nullptr);
  71. va_end (args);
  72. return 1;
  73. }
  74. #endif
  75. #if defined (_WIN32)
  76. static void initPostScriptPage () {
  77. /*
  78. * Save the driver's state.
  79. */
  80. Printer_postScript_printf (nullptr, "/PraatPictureSaveObject save def\n");
  81. /*
  82. * The LaserWriter driver puts the coordinates upside down.
  83. * According to the PostScript Reference Manual,
  84. * "There are few situations in which a PostScript language program
  85. * should execute initgraphics explicitly."
  86. * This is one of them.
  87. BUG: it probably is *not* one of them. Just do something like
  88. currentmatrix [1 0 -1 0 0 0] mul setmatrix
  89. or whatever it is.
  90. */
  91. #if 1
  92. Printer_postScript_printf (nullptr, "initmatrix initclip\n");
  93. #else
  94. Printer_postScript_printf (nullptr, "8 8 scale initclip\n");
  95. #endif
  96. }
  97. static void exitPostScriptPage () {
  98. Printer_postScript_printf (nullptr, "PraatPictureSaveObject restore\n");
  99. }
  100. #endif
  101. #if cocoa
  102. #elif defined (_WIN32)
  103. static void initPrinter () {
  104. }
  105. #endif
  106. void Printer_nextPage () {
  107. #if cocoa
  108. [theMacView endPage];
  109. [theMacView beginPageInRect: [theMacView bounds] atPlacement: NSMakePoint (0, 0)];
  110. #elif defined (_WIN32)
  111. if (thePrinter. postScript) {
  112. exitPostScriptPage ();
  113. EndPage (theWinDC);
  114. StartPage (theWinDC);
  115. initPostScriptPage ();
  116. } else {
  117. if (EndPage (theWinDC) < 0) ; /* BUG: should give the opportunity of cancellation. */
  118. StartPage (theWinDC);
  119. /*
  120. * Since StartPage has reset the DC, restore some of our non-default settings.
  121. */
  122. SetBkMode (theWinDC, TRANSPARENT);
  123. SetTextAlign (theWinDC, TA_LEFT | TA_BASELINE | TA_NOUPDATECP);
  124. }
  125. #endif
  126. }
  127. int Printer_pageSetup () {
  128. #if cocoa
  129. NSPageLayout *cocoaPageSetupDialog = [NSPageLayout pageLayout];
  130. [cocoaPageSetupDialog runModal];
  131. #elif defined (_WIN32)
  132. #endif
  133. return 1;
  134. }
  135. #ifdef _WIN32
  136. static BOOL CALLBACK AbortFunc (HDC hdc, int nCode) {
  137. MSG msg;
  138. (void) hdc;
  139. (void) nCode;
  140. while (PeekMessage (& msg, 0, 0, 0, PM_REMOVE)) {
  141. TranslateMessage (& msg);
  142. DispatchMessage (& msg);
  143. }
  144. return true;
  145. }
  146. HDC Printer_getDC () {
  147. if (! theWinPrint. hDevMode) {
  148. memset (& theWinPrint, 0, sizeof (PRINTDLG));
  149. theWinPrint. lStructSize = sizeof (PRINTDLG);
  150. theWinPrint. Flags = PD_RETURNDEFAULT | PD_RETURNDC;
  151. PrintDlg (& theWinPrint);
  152. }
  153. return theWinPrint. hDC;
  154. }
  155. #endif
  156. #if cocoa
  157. static void (*theDraw) (void *boss, Graphics g);
  158. static void *theBoss;
  159. @interface GuiCocoaPrintingArea : NSView @end
  160. @implementation GuiCocoaPrintingArea {
  161. //GuiButton d_userData;
  162. }
  163. - (void) drawRect: (NSRect) dirtyRect {
  164. trace (U"printing ", dirtyRect. origin. x, U" ", dirtyRect. origin. y, U" ", dirtyRect. size. width, U" ", dirtyRect. size. height);
  165. int currentPage = [[NSPrintOperation currentOperation] currentPage];
  166. {// scope
  167. autoGraphics graphics = Graphics_create_screenPrinter (nullptr, self);
  168. theDraw (theBoss, graphics.get());
  169. }
  170. }
  171. - (BOOL) isFlipped {
  172. return YES;
  173. }
  174. - (NSPoint) locationOfPrintRect: (NSRect) aRect {
  175. (void) aRect;
  176. return NSMakePoint (0.0, 0.0); // the origin of the rect's coordinate system is always the top left corner of the physical page
  177. }
  178. - (BOOL) knowsPageRange: (NSRangePointer) range {
  179. range -> length = 1;
  180. return YES;
  181. }
  182. - (NSRect) rectForPage: (NSInteger) pageNumber {
  183. (void) pageNumber; // every page has the same rectangle
  184. return [self bounds];
  185. }
  186. - (void) printOperationDidRun: (NSPrintOperation *) printOperation success: (BOOL) success contextInfo: (void *) contextInfo {
  187. }
  188. @end
  189. #endif
  190. int Printer_print (void (*draw) (void *boss, Graphics g), void *boss) {
  191. try {
  192. #if defined (UNIX)
  193. structMelderFile tempFile { };
  194. char tempPath_utf8 [] = "/tmp/picXXXXXX";
  195. close (mkstemp (tempPath_utf8));
  196. Melder_pathToFile (Melder_peek8to32 (tempPath_utf8), & tempFile);
  197. {// scope
  198. autoGraphics graphics = Graphics_create_postscriptjob (& tempFile, thePrinter. resolution,
  199. thePrinter. spots, thePrinter. paperSize, thePrinter. orientation, thePrinter. magnification);
  200. draw (boss, graphics.get());
  201. }
  202. char command [500];
  203. sprintf (command, Melder_peek32to8 (Site_getPrintCommand ()), tempPath_utf8);
  204. system (command);
  205. MelderFile_delete (& tempFile);
  206. #elif cocoa
  207. theDraw = draw;
  208. theBoss = boss;
  209. NSPrintInfo *info = [NSPrintInfo sharedPrintInfo];
  210. NSSize paperSize = [info paperSize];
  211. //NSLog (@"%f %f", paperSize. width, paperSize. height);
  212. thePrinter. paperWidth = paperSize. width / 0.12;
  213. thePrinter. paperHeight = paperSize. height / 0.12;
  214. [info setLeftMargin: 0.0];
  215. [info setRightMargin: 0.0];
  216. [info setTopMargin: 0.0];
  217. [info setBottomMargin: 0.0];
  218. /*
  219. * Although the paper size reported may be 595 x 842 points (A4),
  220. * 783 points (just under 11 inches) is the largest height that keeps the view on one page.
  221. */
  222. int viewWidth = paperSize. width;
  223. int viewHeight = paperSize. height;
  224. NSLog (@"%d %d", viewWidth, viewHeight);
  225. NSRect rect = NSMakeRect (0, 0, viewWidth, viewHeight);
  226. NSView *cocoaPrintingArea = [[GuiCocoaPrintingArea alloc] initWithFrame: rect];
  227. theMacView = cocoaPrintingArea;
  228. [cocoaPrintingArea setBoundsSize: NSMakeSize (viewWidth / 0.12, viewHeight / 0.12)]; // 72 points per inch / 600 dpi = 0.12 points per dot
  229. [cocoaPrintingArea setBoundsOrigin: NSMakePoint (0, 0)];
  230. NSPrintOperation *op = [NSPrintOperation
  231. printOperationWithView: cocoaPrintingArea];
  232. #if 1
  233. if (op) [op runOperation];
  234. #else
  235. /*
  236. * This may crash with multiple pages.
  237. */
  238. if (op) {
  239. [op setCanSpawnSeparateThread: NO];
  240. NSView *pictureView = ((GraphicsScreen) Picture_peekGraphics ((Picture) boss)) -> d_macView;
  241. [op
  242. runOperationModalForWindow: [pictureView window]
  243. delegate: cocoaPrintingArea
  244. didRunSelector: @selector(printOperationDidRun:success:contextInfo:)
  245. contextInfo: nil
  246. ];
  247. }
  248. #endif
  249. #elif defined (_WIN32)
  250. int postScriptCode = POSTSCRIPT_PASSTHROUGH;
  251. DOCINFO docInfo;
  252. DEVMODE *devMode;
  253. initPrinter ();
  254. if (! theWinPrint. hDevMode) {
  255. memset (& theWinPrint, 0, sizeof (PRINTDLG));
  256. theWinPrint. lStructSize = sizeof (PRINTDLG);
  257. theWinPrint. Flags = PD_RETURNDEFAULT;
  258. if (! PrintDlg (& theWinPrint)) Melder_throw (U"Cannot initialize printer.");
  259. }
  260. if (Melder_backgrounding) {
  261. theWinPrint. Flags = PD_RETURNDEFAULT | PD_RETURNDC;
  262. if (! PrintDlg (& theWinPrint) || ! theWinPrint. hDC) {
  263. Melder_throw (U"Cannot print from a script on this computer.");
  264. }
  265. } else {
  266. theWinPrint. Flags &= ~ PD_RETURNDEFAULT;
  267. theWinPrint. Flags |= PD_RETURNDC;
  268. if (! PrintDlg (& theWinPrint)) return 1;
  269. }
  270. theWinDC = theWinPrint. hDC;
  271. thePrinter. postScript = thePrinter. allowDirectPostScript &&
  272. Escape (theWinDC, QUERYESCSUPPORT, sizeof (int), (LPSTR) & postScriptCode, nullptr);
  273. /*
  274. * The HP colour inkjet printer returns in dmFields:
  275. * 0, 1, 8, 9, 10, 11, 12, 13, 14, 15, 23, 24, 25, 26 = DM_ORIENTATION |
  276. * DM_PAPERSIZE | DM_COPIES | DM_DEFAULTSOURCE | DM_PRINTQUALITY |
  277. * DM_COLOR | DM_DUPLEX | DM_YRESOLUTION | DM_TTOPTION | DM_COLLATE |
  278. * DM_ICMMETHOD | DM_ICMINTENT | DM_MEDIATYPE | DM_DITHERTYPE
  279. */
  280. devMode = * (DEVMODE **) theWinPrint. hDevMode;
  281. thePrinter. resolution = devMode -> dmFields & DM_YRESOLUTION ? devMode -> dmYResolution :
  282. devMode -> dmFields & DM_PRINTQUALITY ?
  283. ( devMode -> dmPrintQuality > 0 ? devMode -> dmPrintQuality : 300 ) : 300;
  284. if (devMode -> dmFields & DM_PAPERWIDTH) {
  285. thePrinter. paperWidth = devMode -> dmPaperWidth * thePrinter. resolution / 254;
  286. thePrinter. paperHeight = devMode -> dmPaperLength * thePrinter. resolution / 254;
  287. } else if (devMode -> dmFields & DM_PAPERSIZE) {
  288. static struct { float width, height; } sizes [] = { { 0, 0 }, { 8.5, 11 }, { 8.5, 11 }, { 11, 17 },
  289. { 17, 11 }, { 8.5, 14 }, { 5.5, 8.5 }, { 7.25, 10.5 }, { 297/25.4, 420/25.4 },
  290. { 210/25.4, 297/25.4 }, { 210/25.4, 297/25.4 }, { 148.5/25.4, 210/25.4 },
  291. { 250/25.4, 354/25.4 }, { 182/25.4, 257/25.4 }, { 8.5, 13 },
  292. { 215/25.4, 275/25.4 }, { 10, 14 }, { 11, 17 }, { 8.5, 11 }, { 3.875, 8.875 },
  293. { 4.125, 9.5 }, { 4.5, 10.375 } };
  294. int paperSize = devMode -> dmPaperSize;
  295. if (paperSize <= 0 || paperSize > 21) paperSize = 1;
  296. thePrinter. paperWidth = sizes [paperSize]. width * thePrinter. resolution;
  297. thePrinter. paperHeight = sizes [paperSize]. height * thePrinter. resolution;
  298. if (devMode -> dmOrientation == DMORIENT_LANDSCAPE) {
  299. long dummy = thePrinter. paperWidth;
  300. thePrinter. paperWidth = thePrinter. paperHeight;
  301. thePrinter. paperHeight = dummy;
  302. }
  303. } else {
  304. thePrinter. paperWidth = 1000;
  305. thePrinter. paperHeight = 1000;
  306. }
  307. EnableWindow ((HWND) XtWindow (theCurrentPraatApplication -> topShell -> d_xmShell), false);
  308. SetAbortProc (theWinDC, AbortFunc);
  309. memset (& docInfo, 0, sizeof (DOCINFO));
  310. docInfo. cbSize = sizeof (DOCINFO);
  311. docInfo. lpszDocName = L"Praatjes";
  312. docInfo. lpszOutput = nullptr;
  313. if (thePrinter. postScript) {
  314. StartDoc (theWinDC, & docInfo);
  315. StartPage (theWinDC);
  316. initPostScriptPage ();
  317. {// scope
  318. autoGraphics graphics = Graphics_create_postscriptprinter ();
  319. draw (boss, graphics.get());
  320. }
  321. exitPostScriptPage ();
  322. EndPage (theWinDC);
  323. EndDoc (theWinDC);
  324. } else {
  325. StartDoc (theWinDC, & docInfo);
  326. StartPage (theWinDC);
  327. {// scope
  328. autoGraphics graphics = Graphics_create_screenPrinter (nullptr, theWinDC);
  329. draw (boss, graphics.get());
  330. }
  331. if (EndPage (theWinDC) < 0) {
  332. Melder_throw (U"Cannot print page.");
  333. } else {
  334. EndDoc (theWinDC);
  335. }
  336. }
  337. EnableWindow ((HWND) XtWindow (theCurrentPraatApplication -> topShell -> d_xmShell), true);
  338. DeleteDC (theWinDC), theWinDC = nullptr;
  339. #endif
  340. return 1;
  341. } catch (MelderError) {
  342. Melder_throw (U"Not printed.");
  343. }
  344. }
  345. /* End of file Printer.cpp */