summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXPrint.c
blob: ee30e1ff9f5c7bcc64c0af3b4ba06613770703a3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/*
 * tkMacOSXPrint.c --
 *
 *      This module implements native printing dialogs for macOS.
 *
 * Copyright © 2006 Apple Inc.
 * Copyright © 2011-2021 Kevin Walzer/WordTech Communications LLC.
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

#include <tcl.h>
#include <tk.h>
#include <tkInt.h>
#include <Cocoa/Cocoa.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreServices/CoreServices.h>
#include <ApplicationServices/ApplicationServices.h>
#include <tkMacOSXInt.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include "tkMacOSXImage.h"
#include "tkMacOSXPrivate.h"

NSString * fileName = nil;
CFStringRef urlFile = NULL;

/*Forward declaration of functions.*/
static Tcl_ObjCmdProc StartPrint;
static OSStatus	FinishPrint(NSString *file, int buttonValue);
static Tcl_ObjCmdProc MakePDF;
int			MacPrint_Init(Tcl_Interp * interp);

/* Delegate class for print dialogs. */
@interface PrintDelegate: NSObject
    - (id) init;
    - (void) printPanelDidEnd: (NSPrintPanel *) printPanel
		   returnCode: (int) returnCode
		  contextInfo: (void *) contextInfo;
@end

@implementation PrintDelegate
- (id) init {
    self = [super init];
    return self;
}

- (void) printPanelDidEnd: (NSPrintPanel *) printPanel
	       returnCode: (int) returnCode
	      contextInfo: (void *) contextInfo {
    (void) printPanel;
    (void) contextInfo;

    /*
     * Pass returnCode to FinishPrint function to determine how to
     * handle.
     */

    FinishPrint(fileName, returnCode);
}
@end

/*
 *----------------------------------------------------------------------
 *
 * StartPrint --
 *
 * 	Launch native print dialog.
 *
 * Results:
 *	Configures values and starts print process.
 *
 *----------------------------------------------------------------------
 */

int
StartPrint(
    TCL_UNUSED(void *),
    Tcl_Interp * interp,
    int objc,
    Tcl_Obj *const objv[])
{
    NSPrintInfo * printInfo = [NSPrintInfo sharedPrintInfo];
    NSPrintPanel * printPanel = [NSPrintPanel printPanel];
    int accepted;
    PMPrintSession printSession;
    PMPrintSettings printSettings;
    OSStatus status = noErr;

    /* Check for proper number of arguments. */
    if (objc < 2) {
        Tcl_WrongNumArgs(interp, 1, objv, "file");
        return TCL_ERROR;
    }

    fileName = [NSString stringWithUTF8String: Tcl_GetString(objv[1])];
    urlFile = (CFStringRef) fileName;
    CFRetain(urlFile);

    /* Initialize the delegate for the callback from the page panel. */
    PrintDelegate * printDelegate = [[PrintDelegate alloc] init];

    status = PMCreateSession( & printSession);
    if (status != noErr) {
        NSLog(@ "Error creating print session.");
        return TCL_ERROR;
    }

    status = PMCreatePrintSettings( & printSettings);
    if (status != noErr) {
        NSLog(@ "Error creating print settings.");
        return TCL_ERROR;
    }

    status = PMSessionDefaultPrintSettings(printSession, printSettings);
    if (status != noErr) {
        NSLog(@ "Error creating default print settings.");
        return TCL_ERROR;
    }

    printSession = (PMPrintSession)[printInfo PMPrintSession];
    (void)(PMPageFormat)[printInfo PMPageFormat];
    printSettings = (PMPrintSettings)[printInfo PMPrintSettings];

    accepted = (int)[printPanel runModalWithPrintInfo: printInfo];
    [printDelegate printPanelDidEnd: printPanel
			 returnCode: accepted
			contextInfo: printInfo];

    return TCL_OK;
}

/*
 *----------------------------------------------------------------------
 *
 * FinishPrint --
 *
 * 	Handles print process based on input from dialog.
 *
 * Results:
 *	Completes print process.
 *
 *----------------------------------------------------------------------
 */

OSStatus
FinishPrint(
    NSString *file,
    int buttonValue)
{
    NSPrintInfo * printInfo = [NSPrintInfo sharedPrintInfo];
    PMPrintSession printSession;
    PMPageFormat pageFormat;
    PMPrintSettings printSettings;
    OSStatus status = noErr;
    CFStringRef mimeType = NULL;

    /*
     * If value passed here is NSCancelButton, return noErr;
     * otherwise printing will occur regardless of value.
     */
    if (buttonValue == NSModalResponseCancel) {
        return noErr;
    }

    status = PMCreateSession( & printSession);
    if (status != noErr) {
        NSLog(@ "Error creating print session.");
        return status;
    }

    status = PMCreatePrintSettings( & printSettings);
    if (status != noErr) {
        NSLog(@ "Error creating print settings.");
        return status;
    }

    status = PMSessionDefaultPrintSettings(printSession, printSettings);
    if (status != noErr) {
        NSLog(@ "Error creating default print settings.");
        return status;
    }

    printSession = (PMPrintSession)[printInfo PMPrintSession];
    pageFormat = (PMPageFormat)[printInfo PMPageFormat];
    printSettings = (PMPrintSettings)[printInfo PMPrintSettings];

    /*Handle print operation.*/
    if (buttonValue == NSModalResponseOK) {

        if (urlFile == NULL) {
            NSLog(@ "Could not get file to print.");
            return noErr;
        }

        fileName = file;

        CFURLRef printURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, urlFile, kCFURLPOSIXPathStyle, false);

        PMPrinter currentPrinter;
        PMDestinationType printDestination;

        /*Get the intended destination.*/
        status = PMSessionGetDestinationType(printSession, printSettings, & printDestination);

        /*Destination is printer. Send file to printer.*/
        if (status == noErr && printDestination == kPMDestinationPrinter) {

            status = PMSessionGetCurrentPrinter(printSession, & currentPrinter);
            if (status == noErr) {
                CFArrayRef mimeTypes;
                status = PMPrinterGetMimeTypes(currentPrinter, printSettings, & mimeTypes);
                if (status == noErr && mimeTypes != NULL) {
                    mimeType = CFSTR("application/pdf");
                    if (CFArrayContainsValue(mimeTypes, CFRangeMake(0, CFArrayGetCount(mimeTypes)), mimeType)) {
                        status = PMPrinterPrintWithFile(currentPrinter, printSettings, pageFormat, mimeType, printURL);
                        CFRelease(urlFile);
                        return status;
                    }
                }
            }
        }

        /* Destination is file. Determine how to handle. */
        if (status == noErr && printDestination == kPMDestinationFile) {
            CFURLRef outputLocation = NULL;

            status = PMSessionCopyDestinationLocation(printSession, printSettings, & outputLocation);
            if (status == noErr) {
                /*Get the source file and target destination, convert to strings.*/
                CFStringRef sourceFile = CFURLCopyFileSystemPath(printURL, kCFURLPOSIXPathStyle);
                CFStringRef savePath = CFURLCopyFileSystemPath(outputLocation, kCFURLPOSIXPathStyle);
                NSString * sourcePath = (NSString * ) sourceFile;
                NSString * finalPath = (NSString * ) savePath;
                NSString * pathExtension = [finalPath pathExtension];
                NSFileManager * fileManager = [NSFileManager defaultManager];
		NSError * error = nil;

        /*
		 * Is the target file a PDF? If so, copy print file
		 * to output location.
		 */
                if ([pathExtension isEqualToString: @ "pdf"]) {

		    /*Make sure no file conflict exists.*/
		    if ([fileManager fileExistsAtPath: finalPath]) {
			[fileManager removeItemAtPath: finalPath error: &error];
		    }
                    if ([fileManager fileExistsAtPath: sourcePath]) {
                        error = nil;
                        [fileManager copyItemAtPath: sourcePath toPath: finalPath error: & error];
                    }
		    return status;
                }

                /*
                 * Is the target file PostScript? If so, run print file
                 * through CUPS filter to convert back to PostScript.
                 */

              if ([pathExtension isEqualToString: @ "ps"]) {
                    char source[5012];
                    char target[5012];
                    [sourcePath getCString: source maxLength: (sizeof source) encoding: NSUTF8StringEncoding];
                    [finalPath getCString: target maxLength: (sizeof target) encoding: NSUTF8StringEncoding];
		    /*Make sure no file conflict exists.*/
		    if ([fileManager fileExistsAtPath: finalPath]) {
			[fileManager removeItemAtPath: finalPath error: &error];
		    }

		    /*
		     *  Fork and start new process with command string. Thanks to Peter da Silva
		     *  for assistance.
		     */
  		    pid_t pid;
		    if ((pid = fork()) == -1) {
		      return -1;
		    } else if (pid == 0) {
		      /* Redirect output to file and silence debugging output.*/
		      dup2(open(target, O_RDWR | O_CREAT, 0777), 1);
		      dup2(open("/dev/null", O_WRONLY), 2);
		      execl("/usr/sbin/cupsfilter", "/usr/sbin/cupsfilter", "-m", "application/postscript", source, NULL);
		      exit(0);
		    }
	      return status;
	      }
	    }
	}

        /* Destination is preview. Open file in default application for PDF. */
        if ((status == noErr) && (printDestination == kPMDestinationPreview)) {
            CFStringRef urlpath = CFURLCopyFileSystemPath(printURL, kCFURLPOSIXPathStyle);
            NSString * path = (NSString * ) urlpath;
            NSURL * url = [NSURL fileURLWithPath: path];
            NSWorkspace * ws = [NSWorkspace sharedWorkspace];
            [ws openURL: url];
            status = noErr;
            return status;
        }

        /*
         * If destination is not printer, file or preview,
         * we do not support it. Display alert.
         */

	if (((status == noErr) && (printDestination != kPMDestinationPreview)) || ((status == noErr) && (printDestination != kPMDestinationFile)) || ((status == noErr) &&  (printDestination != kPMDestinationPrinter))) {

            NSAlert * alert = [[[NSAlert alloc] init] autorelease];
            [alert addButtonWithTitle: @ "OK"];

            [alert setMessageText: @ "Unsupported Printing Operation"];
            [alert setInformativeText: @ "This printing operation is not supported."];
            [alert setAlertStyle: NSAlertStyleInformational];
            [alert runModal];
            return status;
        }
    }

    /* Return because cancel button was clicked. */
    if (buttonValue == NSModalResponseCancel) {
        PMRelease(printSession);
        return status;
    }

    return status;
}


/*
 *----------------------------------------------------------------------
 *
 * MakePDF--
 *
 * 	Converts a Tk canvas to PDF data.
 *
 * Results:
 *	Outputs PDF file.
 *
 *----------------------------------------------------------------------
 */

int MakePDF(
 TCL_UNUSED(void *),
 Tcl_Interp *ip,
 int objc,
 Tcl_Obj *const objv[])
{
    Tk_Window path;
    Drawable d;
    unsigned int width, height;
    CFDataRef pdfData;

    if (objc != 2) {
	Tcl_WrongNumArgs(ip, 1, objv, "path?");
	return TCL_ERROR;
    }

    /*Get window and render to PDF.*/

    path = Tk_NameToWindow(ip, Tcl_GetString(objv[1]), Tk_MainWindow(ip));
    if (path == NULL) {
	return TCL_ERROR;
    }

    Tk_MakeWindowExist(path);
    Tk_MapWindow(path);
    d = Tk_WindowId(path);
    width = Tk_Width(path);
    height = Tk_Height(path);

    pdfData = CreatePDFFromDrawableRect(d, 0, 0, width, height);

    NSData *viewData = (NSData*)pdfData;
    [viewData writeToFile:@"/tmp/tk_canvas.pdf" atomically:YES];
    return TCL_OK;

}

/*
 *----------------------------------------------------------------------
 *
 * MacPrint_Init--
 *
 * 	Initializes the printing module.
 *
 * Results:
 *	Printing module initialized.
 *
 *----------------------------------------------------------------------
 */

int MacPrint_Init(Tcl_Interp * interp) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    Tcl_CreateObjCommand(interp, "::tk::print::_print", StartPrint, NULL, NULL);
    Tcl_CreateObjCommand(interp, "::tk::print::_printcanvas", MakePDF, NULL, NULL);
    [pool release];
    return TCL_OK;
}

/*
 * Local Variables:
 * mode: objc
 * c-basic-offset: 4
 * fill-column: 79
 * coding: utf-8
 * End:
 */