Dear community, how can I get the user-defined printer settings presets programmatically?
PMPrinterCopyPresets() API only gets the pre-defined presets for a given printer, but not the ones added by the user by means of the standard print dialog.
Dear community, how can I get the user-defined printer settings presets programmatically?
PMPrinterCopyPresets() API only gets the pre-defined presets for a given printer, but not the ones added by the user by means of the standard print dialog.
Per-printer and common user-defined print presets are saved in plist files located in ~/Library/Preferences/.
For common presets the file is: com.apple.print.custompapers.plist .
For per-printer ones the files are: com.apple.print.custompresets.forprinter.PRINTER_NAME.plist .
To use the preset settings the following steps can be performed:
// Sample code
// ut_print_presets_system.h : PrintPresetsSystem class definition
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
#pragma once
namespace ut
{
class PrintPreset;
class PrintData;
// manages platform-specific print presets stored
class UTILS_API PrintPresetsSystem
{
public:
static PrintPresetsSystem& instance();
void load(const wxString& printerName);
void reload(const wxString& printerName);
unsigned int size() const; // "None" preset is counted
unsigned int getIndex(const wxString& name) const;
wxString getName(unsigned int index) const;
bool presetToPrintData(const wxString& name, PrintData& printData);
private:
PrintPresetsSystem();
PrintPresetsSystem(const PrintPresetsSystem&) = delete;
PrintPresetsSystem& operator=(const PrintPresetsSystem&) = delete;
~PrintPresetsSystem();
void loadBuiltin(const wxString&);
void loadCustom(const wxString&, bool);
void clear();
void append(PrintPreset*);
private:
wxString m_printerName;
std::vector<PrintPreset*> m_printPresets;
};
} // namespace ut
// ut_print_presets_system_mac.mm : PrintPresetsSystem class implementation for mac
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
#include "ut_precompiled.h"
#include "ut_print_presets_system.h"
#include <wx/osx/core/cfstring.h>
#import <Foundation/Foundation.h>
namespace ut
{
#pragma mark - Local defines
static const wxString s_nonePresetName = _("None");
#define kPMPresetFileCommon @"com.apple.print.custompresets"
#define kPMPresetNamesKey @"com.apple.print.customPresetNames"
#define kPMPresetSettingsKey @"com.apple.print.preset.settings"
#define kPMPresetPaperInfoKey @"com.apple.print.preset.PaperInfo"
#define kPMPresetOrientationKey @"com.apple.print.preset.Orientation"
#define kPMPresetScalingKey @"com.apple.print.preset.Scaling"
#define kPMPaperNameKey @"com.apple.print.PaperInfo.PMPaperName"
#if 0 // so far unused keys
#define kPMCustomPaperKey @"com.apple.print.PaperInfo.PMCustomPaper"
#define kPMUnadjustedPaperRectKey @"com.apple.print.PaperInfo.PMUnadjustedPaperRect"
#define kPMUnadjustedPageRectKey @"com.apple.print.PaperInfo.PMUnadjustedPageRect"
#define kPMAdjustedPaperRectKey @"com.apple.print.PageFormat.PMAdjustedPaperRect"
#define kPMAdjustedPageRectKey @"com.apple.print.PageFormat.PMAdjustedPageRect"
#endif // 0
#pragma mark - Local helpers
static NSString* getCustomPrintPresetsFolder()
{
return [@"~/Library/Preferences" stringByExpandingTildeInPath];
}
static bool printSettingsToPrintInfo(NSDictionary* inputDict, WX_NSPrintInfo& printInfo)
{
// it is ok for a print preset dictionary to have no paper info, for example
// the "Black and White" preset only contains {ColorModel,Duplex,cupsPrintQuality} keys
NSDictionary* root = [inputDict objectForKey:kPMPresetPaperInfoKey];
if (root == nil)
return true;
NSDictionary* dict = [root objectForKey:@"paperInfo"];
if (dict == nil)
return true;
// init
PMPageFormat pageFormat = (PMPageFormat)[printInfo PMPageFormat];
PMPrintSession printSession = (PMPrintSession)[printInfo PMPrintSession];
PMPrinter printer;
OSStatus status = PMSessionGetCurrentPrinter(printSession, &printer);
NSString* paperName = [dict objectForKey:kPMPaperNameKey];
if (status != noErr || paperName == nil || [paperName length] == 0)
return false;
// re-create print info's page format based on paper name from dictionary
CFArrayRef paperlist = nullptr;
if (PMPrinterGetPaperList(printer, &paperlist) == noErr)
{
PMPaper matchedPaper = kPMNoData;
CFIndex papersCount = CFArrayGetCount(paperlist);
for (CFIndex index = 0; index < papersCount; index++)
{
PMPaper paper = (PMPaper)CFArrayGetValueAtIndex(paperlist, index);
CFStringRef currentPaperName = nullptr;
if (PMPaperGetID(paper, ¤tPaperName) != noErr)
continue;
if ([paperName compare:(NSString*)currentPaperName] == NSOrderedSame)
{
matchedPaper = paper;
break;
}
}
if (matchedPaper != kPMNoData)
{
PMPageFormat newPageFormat;
PMCreatePageFormatWithPMPaper(&newPageFormat, matchedPaper);
PMCopyPageFormat(newPageFormat, pageFormat);
PMRelease(newPageFormat);
}
else
return false;
}
// update print info's orientation and scale based on values from dictionary
PMOrientation orientation = [[inputDict objectForKey:kPMPresetOrientationKey] unsignedShortValue];
if (PMSetOrientation(pageFormat, orientation, kPMUnlocked) != noErr)
return false;
#if 0 // we don't have GUI for scale expressed in percent or as a factor
double scale = [[inputDict objectForKey:kPMPresetScalingKey] doubleValue];
if (PMSetScale(pageFormat, scale) != noErr)
return false;
#endif // 0
[printInfo updateFromPMPageFormat];
return true;
}
#pragma mark - CustomPrintPreset class
class PrintPreset
{
public:
static PrintPreset* create(NSString* key, NSDictionary* dict);
~PrintPreset() { [m_dict release]; }
wxString getName() const { return m_name; }
bool toPrintData(PrintData& printData) const;
private:
PrintPreset(const wxString& name, NSDictionary* dict): m_name(name), m_dict(dict) { [m_dict retain]; }
private:
wxString m_name;
NSDictionary* m_dict = nil;
};
PrintPreset* PrintPreset::create(NSString* key, NSDictionary* dict)
{
if (dict == nil)
return nullptr;
wxString wxName = wxCFStringRef::AsString(key);
return wxName.IsEmpty() ? nullptr : new PrintPreset(wxName, dict);;
}
bool PrintPreset::toPrintData(PrintData& printData) const
{
MacPrintNativeData* pNativeData = MacPrintNativeData::GetNativeData(printData);
NSDictionary* printSettingsDict = [m_dict objectForKey:kPMPresetSettingsKey];
if (pNativeData == nullptr || printSettingsDict == nil)
return false;
// adds & overwrites entries, but does not clear the receiving dictionary
[[pNativeData->m_macPrintInfo printSettings] addEntriesFromDictionary:printSettingsDict];
// additionally update paper info, as e.g. preset paper format is stored in printer settings
if (!printSettingsToPrintInfo(printSettingsDict, pNativeData->m_macPrintInfo))
ut::messageBox(_("Page format defined in print preset cannot be used."));
if (PMGetPageFormatPaper(pNativeData->m_macPageFormat, &pNativeData->m_macPaper) != noErr)
return false;
if (PMSessionValidatePageFormat(pNativeData->m_macPrintSession,
pNativeData->m_macPageFormat, kPMDontWantBoolean) != noErr)
return false;
if (PMSessionValidatePrintSettings(pNativeData->m_macPrintSession,
pNativeData->m_macPrintSettings, kPMDontWantBoolean) != noErr)
return false;
printData.ConvertFromNative();
return true;
}
#pragma mark - PrintPresetsSystem class implementation
PrintPresetsSystem::PrintPresetsSystem()
{
}
PrintPresetsSystem::~PrintPresetsSystem()
{
clear();
}
PrintPresetsSystem& PrintPresetsSystem::instance()
{
static PrintPresetsSystem s_theInstance;
return s_theInstance;
}
void PrintPresetsSystem::load(const wxString& printerName)
{
if (m_printerName == printerName)
return;
else
{
clear();
m_printerName = printerName;
}
// 1. load builtuin presets, i.e. defined in OS or printer driver
loadBuiltin(printerName);
// 2. load custom specific presets, i.e. user-defined presets for the given printer
loadCustom(printerName, true);
// 3. load custom common presets, i.e. user-defined presets for all printers
loadCustom(printerName, false);
}
void PrintPresetsSystem::reload(const wxString& printerName)
{
clear();
load(printerName);
}
void PrintPresetsSystem::loadBuiltin(const wxString& printerName)
{
PMPrinter printer = PMPrinterCreateFromPrinterID(wxCFStringRef(printerName));
if (printer == nullptr)
return;
CFArrayRef presetsList = nullptr;
OSStatus status = PMPrinterCopyPresets(printer, &presetsList);
PMRelease(printer);
if (status != noErr || presetsList == nullptr)
return;
CFIndex arrayCount = CFArrayGetCount(presetsList);
for (CFIndex index = 0; index < arrayCount; index++)
{
PMPreset preset = (PMPreset)CFArrayGetValueAtIndex(presetsList, index);
CFStringRef presetName = nullptr;
if (PMPresetCopyName(preset, &presetName) == noErr && CFStringGetLength(presetName) > 0)
{
NSDictionary* dict = nil;
if (PMPresetGetAttributes(preset, (CFDictionaryRef*)(&dict)) == noErr)
{
PrintPreset* pPreset = PrintPreset::create((NSString*)presetName, dict);
if (pPreset != nullptr)
append(pPreset);
}
CFRelease(presetName);
}
}
CFRelease(presetsList);
}
void PrintPresetsSystem::loadCustom(const wxString& printerName, bool perPrinter)
{
NSMutableString* plistFile = [NSMutableString string];
[plistFile appendFormat:@"%@/%@", getCustomPrintPresetsFolder(), kPMPresetFileCommon];
if (perPrinter && !printerName.IsEmpty())
[plistFile appendFormat:@".forprinter.%@", wxCFStringRef(printerName).AsNSString()];
[plistFile appendString:@".plist"];
NSDictionary* rootDict = [[[NSDictionary alloc] initWithContentsOfFile:(NSString*)plistFile] autorelease];
if (rootDict == nil)
return;
NSDictionary* namesDict = [rootDict objectForKey:kPMPresetNamesKey];
for (NSString* key in namesDict)
{
NSDictionary* subDictionary = [rootDict objectForKey:key];
PrintPreset* pPreset = PrintPreset::create(key, subDictionary);
if (pPreset != nullptr)
append(pPreset);
}
}
void PrintPresetsSystem::clear()
{
for (size_t index = 0; index < m_printPresets.size(); index++)
delete m_printPresets[index];
m_printPresets.clear();
m_printerName.Clear();
}
void PrintPresetsSystem::append(PrintPreset* pPreset)
{
m_printPresets.push_back(pPreset);
}
unsigned int PrintPresetsSystem::size() const
{
return m_printPresets.size() + 1; // "None" preset is counted
}
wxString PrintPresetsSystem::getName(unsigned int index) const
{
wxASSERT_MSG(index <= size(), _T("Invalid print preset index"));
return (index == 0) ? s_nonePresetName : m_printPresets[index - 1]->getName();
}
unsigned int PrintPresetsSystem::getIndex(const wxString& name) const
{
if (name == wxEmptyString || name == s_nonePresetName)
return 0;
for (size_t index = 0; index < m_printPresets.size(); index++)
if (m_printPresets[index]->getName() == name)
return index + 1;
wxASSERT_MSG(false, wxString::Format(_T("Print preset not found: %ls"), name.c_str()));
return 0;
}
bool PrintPresetsSystem::presetToPrintData(const wxString& name, PrintData& printData)
{
unsigned int index = getIndex(name);
if (index == 0)
return false;
return m_printPresets[index - 1]->toPrintData(printData);
}
} // namespace ut
Per-printer and common user-defined print presets are saved in plist files located in ~/Library/Preferences/.
For common presets the file is: com.apple.print.custompapers.plist .
For per-printer ones the files are: com.apple.print.custompresets.forprinter.PRINTER_NAME.plist .
To use the preset settings the following steps can be performed:
// Sample code
// ut_print_presets_system.h : PrintPresetsSystem class definition
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
#pragma once
namespace ut
{
class PrintPreset;
class PrintData;
// manages platform-specific print presets stored
class UTILS_API PrintPresetsSystem
{
public:
static PrintPresetsSystem& instance();
void load(const wxString& printerName);
void reload(const wxString& printerName);
unsigned int size() const; // "None" preset is counted
unsigned int getIndex(const wxString& name) const;
wxString getName(unsigned int index) const;
bool presetToPrintData(const wxString& name, PrintData& printData);
private:
PrintPresetsSystem();
PrintPresetsSystem(const PrintPresetsSystem&) = delete;
PrintPresetsSystem& operator=(const PrintPresetsSystem&) = delete;
~PrintPresetsSystem();
void loadBuiltin(const wxString&);
void loadCustom(const wxString&, bool);
void clear();
void append(PrintPreset*);
private:
wxString m_printerName;
std::vector<PrintPreset*> m_printPresets;
};
} // namespace ut
// ut_print_presets_system_mac.mm : PrintPresetsSystem class implementation for mac
//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
#include "ut_precompiled.h"
#include "ut_print_presets_system.h"
#include <wx/osx/core/cfstring.h>
#import <Foundation/Foundation.h>
namespace ut
{
#pragma mark - Local defines
static const wxString s_nonePresetName = _("None");
#define kPMPresetFileCommon @"com.apple.print.custompresets"
#define kPMPresetNamesKey @"com.apple.print.customPresetNames"
#define kPMPresetSettingsKey @"com.apple.print.preset.settings"
#define kPMPresetPaperInfoKey @"com.apple.print.preset.PaperInfo"
#define kPMPresetOrientationKey @"com.apple.print.preset.Orientation"
#define kPMPresetScalingKey @"com.apple.print.preset.Scaling"
#define kPMPaperNameKey @"com.apple.print.PaperInfo.PMPaperName"
#if 0 // so far unused keys
#define kPMCustomPaperKey @"com.apple.print.PaperInfo.PMCustomPaper"
#define kPMUnadjustedPaperRectKey @"com.apple.print.PaperInfo.PMUnadjustedPaperRect"
#define kPMUnadjustedPageRectKey @"com.apple.print.PaperInfo.PMUnadjustedPageRect"
#define kPMAdjustedPaperRectKey @"com.apple.print.PageFormat.PMAdjustedPaperRect"
#define kPMAdjustedPageRectKey @"com.apple.print.PageFormat.PMAdjustedPageRect"
#endif // 0
#pragma mark - Local helpers
static NSString* getCustomPrintPresetsFolder()
{
return [@"~/Library/Preferences" stringByExpandingTildeInPath];
}
static bool printSettingsToPrintInfo(NSDictionary* inputDict, WX_NSPrintInfo& printInfo)
{
// it is ok for a print preset dictionary to have no paper info, for example
// the "Black and White" preset only contains {ColorModel,Duplex,cupsPrintQuality} keys
NSDictionary* root = [inputDict objectForKey:kPMPresetPaperInfoKey];
if (root == nil)
return true;
NSDictionary* dict = [root objectForKey:@"paperInfo"];
if (dict == nil)
return true;
// init
PMPageFormat pageFormat = (PMPageFormat)[printInfo PMPageFormat];
PMPrintSession printSession = (PMPrintSession)[printInfo PMPrintSession];
PMPrinter printer;
OSStatus status = PMSessionGetCurrentPrinter(printSession, &printer);
NSString* paperName = [dict objectForKey:kPMPaperNameKey];
if (status != noErr || paperName == nil || [paperName length] == 0)
return false;
// re-create print info's page format based on paper name from dictionary
CFArrayRef paperlist = nullptr;
if (PMPrinterGetPaperList(printer, &paperlist) == noErr)
{
PMPaper matchedPaper = kPMNoData;
CFIndex papersCount = CFArrayGetCount(paperlist);
for (CFIndex index = 0; index < papersCount; index++)
{
PMPaper paper = (PMPaper)CFArrayGetValueAtIndex(paperlist, index);
CFStringRef currentPaperName = nullptr;
if (PMPaperGetID(paper, ¤tPaperName) != noErr)
continue;
if ([paperName compare:(NSString*)currentPaperName] == NSOrderedSame)
{
matchedPaper = paper;
break;
}
}
if (matchedPaper != kPMNoData)
{
PMPageFormat newPageFormat;
PMCreatePageFormatWithPMPaper(&newPageFormat, matchedPaper);
PMCopyPageFormat(newPageFormat, pageFormat);
PMRelease(newPageFormat);
}
else
return false;
}
// update print info's orientation and scale based on values from dictionary
PMOrientation orientation = [[inputDict objectForKey:kPMPresetOrientationKey] unsignedShortValue];
if (PMSetOrientation(pageFormat, orientation, kPMUnlocked) != noErr)
return false;
#if 0 // we don't have GUI for scale expressed in percent or as a factor
double scale = [[inputDict objectForKey:kPMPresetScalingKey] doubleValue];
if (PMSetScale(pageFormat, scale) != noErr)
return false;
#endif // 0
[printInfo updateFromPMPageFormat];
return true;
}
#pragma mark - CustomPrintPreset class
class PrintPreset
{
public:
static PrintPreset* create(NSString* key, NSDictionary* dict);
~PrintPreset() { [m_dict release]; }
wxString getName() const { return m_name; }
bool toPrintData(PrintData& printData) const;
private:
PrintPreset(const wxString& name, NSDictionary* dict): m_name(name), m_dict(dict) { [m_dict retain]; }
private:
wxString m_name;
NSDictionary* m_dict = nil;
};
PrintPreset* PrintPreset::create(NSString* key, NSDictionary* dict)
{
if (dict == nil)
return nullptr;
wxString wxName = wxCFStringRef::AsString(key);
return wxName.IsEmpty() ? nullptr : new PrintPreset(wxName, dict);;
}
bool PrintPreset::toPrintData(PrintData& printData) const
{
MacPrintNativeData* pNativeData = MacPrintNativeData::GetNativeData(printData);
NSDictionary* printSettingsDict = [m_dict objectForKey:kPMPresetSettingsKey];
if (pNativeData == nullptr || printSettingsDict == nil)
return false;
// adds & overwrites entries, but does not clear the receiving dictionary
[[pNativeData->m_macPrintInfo printSettings] addEntriesFromDictionary:printSettingsDict];
// additionally update paper info, as e.g. preset paper format is stored in printer settings
if (!printSettingsToPrintInfo(printSettingsDict, pNativeData->m_macPrintInfo))
ut::messageBox(_("Page format defined in print preset cannot be used."));
if (PMGetPageFormatPaper(pNativeData->m_macPageFormat, &pNativeData->m_macPaper) != noErr)
return false;
if (PMSessionValidatePageFormat(pNativeData->m_macPrintSession,
pNativeData->m_macPageFormat, kPMDontWantBoolean) != noErr)
return false;
if (PMSessionValidatePrintSettings(pNativeData->m_macPrintSession,
pNativeData->m_macPrintSettings, kPMDontWantBoolean) != noErr)
return false;
printData.ConvertFromNative();
return true;
}
#pragma mark - PrintPresetsSystem class implementation
PrintPresetsSystem::PrintPresetsSystem()
{
}
PrintPresetsSystem::~PrintPresetsSystem()
{
clear();
}
PrintPresetsSystem& PrintPresetsSystem::instance()
{
static PrintPresetsSystem s_theInstance;
return s_theInstance;
}
void PrintPresetsSystem::load(const wxString& printerName)
{
if (m_printerName == printerName)
return;
else
{
clear();
m_printerName = printerName;
}
// 1. load builtuin presets, i.e. defined in OS or printer driver
loadBuiltin(printerName);
// 2. load custom specific presets, i.e. user-defined presets for the given printer
loadCustom(printerName, true);
// 3. load custom common presets, i.e. user-defined presets for all printers
loadCustom(printerName, false);
}
void PrintPresetsSystem::reload(const wxString& printerName)
{
clear();
load(printerName);
}
void PrintPresetsSystem::loadBuiltin(const wxString& printerName)
{
PMPrinter printer = PMPrinterCreateFromPrinterID(wxCFStringRef(printerName));
if (printer == nullptr)
return;
CFArrayRef presetsList = nullptr;
OSStatus status = PMPrinterCopyPresets(printer, &presetsList);
PMRelease(printer);
if (status != noErr || presetsList == nullptr)
return;
CFIndex arrayCount = CFArrayGetCount(presetsList);
for (CFIndex index = 0; index < arrayCount; index++)
{
PMPreset preset = (PMPreset)CFArrayGetValueAtIndex(presetsList, index);
CFStringRef presetName = nullptr;
if (PMPresetCopyName(preset, &presetName) == noErr && CFStringGetLength(presetName) > 0)
{
NSDictionary* dict = nil;
if (PMPresetGetAttributes(preset, (CFDictionaryRef*)(&dict)) == noErr)
{
PrintPreset* pPreset = PrintPreset::create((NSString*)presetName, dict);
if (pPreset != nullptr)
append(pPreset);
}
CFRelease(presetName);
}
}
CFRelease(presetsList);
}
void PrintPresetsSystem::loadCustom(const wxString& printerName, bool perPrinter)
{
NSMutableString* plistFile = [NSMutableString string];
[plistFile appendFormat:@"%@/%@", getCustomPrintPresetsFolder(), kPMPresetFileCommon];
if (perPrinter && !printerName.IsEmpty())
[plistFile appendFormat:@".forprinter.%@", wxCFStringRef(printerName).AsNSString()];
[plistFile appendString:@".plist"];
NSDictionary* rootDict = [[[NSDictionary alloc] initWithContentsOfFile:(NSString*)plistFile] autorelease];
if (rootDict == nil)
return;
NSDictionary* namesDict = [rootDict objectForKey:kPMPresetNamesKey];
for (NSString* key in namesDict)
{
NSDictionary* subDictionary = [rootDict objectForKey:key];
PrintPreset* pPreset = PrintPreset::create(key, subDictionary);
if (pPreset != nullptr)
append(pPreset);
}
}
void PrintPresetsSystem::clear()
{
for (size_t index = 0; index < m_printPresets.size(); index++)
delete m_printPresets[index];
m_printPresets.clear();
m_printerName.Clear();
}
void PrintPresetsSystem::append(PrintPreset* pPreset)
{
m_printPresets.push_back(pPreset);
}
unsigned int PrintPresetsSystem::size() const
{
return m_printPresets.size() + 1; // "None" preset is counted
}
wxString PrintPresetsSystem::getName(unsigned int index) const
{
wxASSERT_MSG(index <= size(), _T("Invalid print preset index"));
return (index == 0) ? s_nonePresetName : m_printPresets[index - 1]->getName();
}
unsigned int PrintPresetsSystem::getIndex(const wxString& name) const
{
if (name == wxEmptyString || name == s_nonePresetName)
return 0;
for (size_t index = 0; index < m_printPresets.size(); index++)
if (m_printPresets[index]->getName() == name)
return index + 1;
wxASSERT_MSG(false, wxString::Format(_T("Print preset not found: %ls"), name.c_str()));
return 0;
}
bool PrintPresetsSystem::presetToPrintData(const wxString& name, PrintData& printData)
{
unsigned int index = getIndex(name);
if (index == 0)
return false;
return m_printPresets[index - 1]->toPrintData(printData);
}
} // namespace ut