diff options
Diffstat (limited to 'macosx/tkMacOSXPrint.c')
-rw-r--r-- | macosx/tkMacOSXPrint.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/macosx/tkMacOSXPrint.c b/macosx/tkMacOSXPrint.c new file mode 100644 index 0000000..38cd1ca --- /dev/null +++ b/macosx/tkMacOSXPrint.c @@ -0,0 +1,353 @@ +/* + * 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> + +/* Forward declarations of functions and variables. */ +NSString * fileName = nil; +CFStringRef urlFile = NULL; +int StartPrint(ClientData clientData, Tcl_Interp * interp, + int objc, Tcl_Obj * const objv[]); +OSStatus FinishPrint(NSString *file, int buttonValue); +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 { + /* + * 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( + ClientData clientData, + Tcl_Interp * interp, + int objc, + Tcl_Obj *const objv[]) +{ + (void) clientData; + NSPrintInfo * printInfo = [NSPrintInfo sharedPrintInfo]; + NSPrintPanel * printPanel = [NSPrintPanel printPanel]; + int accepted; + PMPrintSession printSession; + PMPageFormat pageFormat; + 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]; + pageFormat = (PMPageFormat)[printInfo PMPageFormat]; + printSettings = (PMPrintSettings)[printInfo PMPrintSettings]; + + accepted = [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; +} + +/* + *---------------------------------------------------------------------- + * + * 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, (ClientData) NULL, (Tcl_CmdDeleteProc * ) NULL); + [pool release]; + return TCL_OK; +} + +/* + * Local Variables: + * mode: objc + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |