Ninjas are deadly. Chipmunk Ninjas are just weird.
About this blog
Marc Travels
Marc on Twitter
JustLooking on Twitter

Marc Wandschneider is a professional software developer with well over fifteen years of industry experience (yes, he really is that old). He travels the globe working on interesting projects and gives talks at conferences and trade shows whenever possible.

My Publications:

My book, "Core Web Application Programming with PHP and MySQL" is now available everywhere, including

My "PHP and MySQL LiveLessons" DVD Series has just been published by Prentice-Hall, and can be purchased on Amazon, through Informit, or Safari

Oct 26, 2006 | 07:15:59
Cocoa Programming: How to fetch Paper Names, Localised Names, and Page Sizes for a Printer
By marcwan

For a recent project written in Cocoa for Mac OS X, I found myself wanting to get the following set of information for a particular printer:

  • A list of all available paper types
  • Printable names for these paper types
  • Page Sizes
  • Imageable Margins for those same pages.

It turns out that there is no way to get this in Cocoa, and actually it’s not all that easy to find this information in Carbon either.

So, after some research and investimigation on the Intarwebs, as well as much combing through the various PM* header files (buried so deep in a directory on my machine that I had to save the directory name somewhere so I’d be able to find them again!), I’ve come up with the following method to do all the above.

It returns an NSArray. Each paper type supported by the printer gets one item in the array, which is an NSDictionary set-up to use key/value pairs. There are the following pairs:

  • PAPER_NAME (NSString): The system name for the paper.
  • LOCALISED_PAPER_NAME (NSString): The display name for the paper.
  • PAPER_SIZE (NSValue): The size of the paper in 72dpi User Space Units. Use [NSValue -sizeValue] to get the actual value.
  • PAPER_IMAGEABLE_MARGINS (NSValue): The imageable bounds of the paper in 72dpi User Space Units. Use [NSValue -rectValue] to get the actual value.

I’ve noticed that there are some issues with memory allocation in the following code: If I run it a few hundred thousand times, I start to see some memory leaks, which is most upsetting. Given that I can’t free anything else in this code without crashing it, I worry that it might be a system leak. However, given how rarely this code is ever executed (maaaaayyybe 5-10x per process lifetime in an extremely print happy case), It’s something that can be investigated later.

Here be the code, laddies:

+ (NSArray *)paperSizesAndNamesForPrinter: (NSString *)printerName
    PMPrintSession printSession = NULL;
    CFArrayRef printerList = NULL;
    PMPrinter thisPrinter = NULL;
    int numPrinters;
    OSStatus err;
    int i;

    err = PMCreateSession(&printSession);
    if (err != noErr)
        [JLUnknownPrinterErrorException generate];

         * Get a list of all printers.
        err = PMServerCreatePrinterList(kPMServerLocal, &printerList);
        if (err != noErr)
            [JLUnknownPrinterErrorException generate];

         * Now, loop through them until we find the printer we're looking
         * for.  Then we can get the properties.  We throw if the printer
         * isn't found.
        numPrinters = CFArrayGetCount(printerList);
        for (i = 0; i < numPrinters; i++)
            CFStringRef thisPrinterName;
            thisPrinter = (PMPrinter)CFArrayGetValueAtIndex(printerList, i);

            thisPrinterName = PMPrinterGetName(thisPrinter);
            if ([(NSString *)thisPrinterName caseInsensitiveCompare: printerName] == NSOrderedSame)
                return [NSPrinter extractInfoFromPrinter: thisPrinter];

            thisPrinter = NULL;
         * Clean up.  Exceptions will still throw back up the stack.
        if (printerList != NULL) CFRelease(printerList);

    return nil;

+ (NSArray *)extractInfoFromPrinter: (PMPrinter)printerInfo
    NSMutableArray *outputArray = [NSMutableArray arrayWithCapacity: 10];
    CFStringRef paperName = NULL, localised = NULL;
    NSMutableDictionary *paperProps;
    CFArrayRef paperList;
    PMPaper thisPaper;
    int j, numPapers;
    OSStatus err;

     * First, get a list of the paper sizes and create an NSArray 
     * for them all.
    err = PMPrinterGetPaperList(printerInfo, &paperList);
    if (err != noErr)
        [JLUnknownPrinterErrorException generate];

        numPapers = CFArrayGetCount(paperList);
        for (j = 0; j < numPapers; j++)
            NSValue *imageableMargins, *paperSize;
            double paperHeight, paperWidth;
            NSRect adjustedMargins;
            PMPaperMargins margie;

            thisPaper =  (PMPaper)CFArrayGetValueAtIndex(paperList, j);

             * Get the system non-localised paper name.
            PMPaperGetID(thisPaper, &paperName);

             * Get the localised paper name.
            err = PMPaperGetName(thisPaper, &localised);
            if (err != noErr)
                [JLUnknownPrinterErrorException generate];

             * Finally, build up the size.
            err = PMPaperGetHeight(thisPaper, &paperHeight);
            if (err != noErr)
                [JLUnknownPrinterErrorException generate];
            err = PMPaperGetWidth(thisPaper, &paperWidth);
            if (err != noErr)
                [JLUnknownPrinterErrorException generate];

            paperSize = [NSValue valueWithSize: NSMakeSize(paperWidth, paperHeight)];

             * This gets the imageable margins for the page type, which
             * is very useful.
            PMPaperGetMargins(thisPaper, &margie);
            adjustedMargins = NSMakeRect(margie.left,,
                                         (paperWidth - margie.left - margie.right),
                                         (paperHeight - - margie.bottom));
            imageableMargins = [NSValue valueWithRect: adjustedMargins];

             * Finally, create a dictionary with these values and then add
             * them to da array.
            paperProps = [NSMutableDictionary dictionaryWithCapacity: 3];
            [paperProps setValue: [NSString stringWithString: (NSString *)paperName]
                        forKey: PAPER_NAME];
            [paperProps setValue: [NSString stringWithString: (NSString *)localised]
                        forKey: LOCALISED_PAPER_NAME];
            [paperProps setValue: paperSize forKey: PAPER_SIZE];
            [paperProps setValue: imageableMargins forKey: PAPER_IMAGEABLE_MARGINS];

            [outputArray addObject: paperProps];

            paperName = NULL;
            localised = NULL;
        if (paperName) CFRelease(paperName);
        if (localised) CFRelease(localised);

    return outputArray;

I actually implemented this as a category on NSPrinter. You can do this too by simply creating the following header:

#import <Cocoa/Cocoa.h>

#define PAPER_NAME               @"paper_name"
#define LOCALISED_PAPER_NAME     @"localised_paper_name"
#define LOCALIZED_PAPER_NAME     @"localised_paper_name"
#define PAPER_SIZE               @"paper_size"
#define PAPER_IMAGEABLE_MARGINS  @"printable_margins"

@interface NSPrinter (MyNSPrinterExtensions)

// returns an NSArray of NSDictionaries: see constants above.
+ (NSArray *)paperSizesAndNamesForPrinter: (NSString *)printerName;
+ (NSArray *)extractInfoFromPrinter: (PMPrinter)printerInfo;


Finally, you’d then wrap the above two functions in

@implementation NSPrinter (MyNSPrinterExtensions)



Sorry if you’ve got a small screen and this article scrolls horizontally. I’ve decided that, unlike other languages that I can easily keep to 80 columns (or even 65 for writing articles), Objective-C simply requires huge amounts of horizontal real estate.

Also, like most of my code, this uses the new Objective-C structured exception handling, which requires Mac OS X 10.3.9 or greater. If you’re not using it, you should be. I was slow to get into SEH when I first saw them many years back, but now I feel any language is incomplete without them.

Exactly what I needed.
Posted By: Dan Messing Nov 08, 2006 14:10:19
Thanks for posting this, it really saved me some time - I was just about to start digging through those header files myself!
Hmm... spoke too soon?
Posted By: Dan Messing Nov 08, 2006 14:35:40
It looks like the localized paper name and the paper name are reversed for some printers... looking into it more now...
¡¡¡Oh Noes!!!
Posted By: marcwan Nov 09, 2006 06:50:38

Please let me know what you find. That would totally be weak if the APIs were wonky. I've only got about 3 printers here on which I can test. I'll harrass some friends and see what their machines say.

Memory Leak
Posted By: marcwan Jan 18, 2010 02:57:32
Yep, good catch. Thanks to Joakim for finding that.

Add a Comment





Copyright © 2005-2008 Marc Wandschneider All Rights Reserved.