高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technote 1131

Creating Desktop Printers on the Fly


目次

概要

デスクトッププリンティングとは

環境のチェック

アップルプリンタ用デスクトッププリンタの作成

StyleWriter

LaserWriter 8.4.3 およびそれ以前 (PAP のみ)

LaserWriter 8.5.1 以降 (PAP、LPR、赤外線、保留、トランスレータ、カスタム)

まとめ

の TECHNOTE では、ユーザにセレクタを使わせないで、プログラムが動作中にデスクトッププリンタを作成する方法を説明します。

注意: ユーザに何も知らせずにアプリケーションでこの方法を使用することはおすすめしません。しかしアップルでは、サポートする方法を文書化しておかないと、ハッキングによる方法が横行し、今後互換性に問題が発生すると考え、この TECHNOTE を作成しました。

プログラムでユーザの DTP (デスクトッププリンタ) を作成した場合は必ずユーザが設定していた DTP に戻し、他のアプリケーションのプリンタ選択状態を妨げないようにしてください。

 


概要

1994 年、アップルは StyleWriter 1200 ドライバと LaserWriter 8.3 ドライバで、デスクトッププリンティングを導入しました。これ以来、多くのデベロッパが Worldwide Developer Technical Support に、ユーザにセレクタを使わせないで、プログラムの動作中にデスクトッププリンタを作成する方法を問い合わせてきました。LaserWriter 8.5.1 のリリースで、アップルはデスクトッププリンタユーティリティというアプリケーションを導入し、ユーザがセレクタを使わないでデスクトッププリンタを作成できるようにしました。

SettingsLib は PrintingLib ファイルに含まれるコードフラグメントです。SettingsLib は、PrintingLib 内の他のコードフラグメントと LaserWriter 8 が、保存されたプリンタ情報にアクセスするために使用します。SettingsLib API にアクセスするためには、PrintingLib とリンクする必要があります。

デスクトッププリンティングとは

デスクトッププリンティングとは、ドキュメントをユーザのデスクトップ上のデスクトッププリンタのアイコンにドラッグ&ドロップしてプリントするメカニズムです。サポートしているのは、Mac OS 8.x (組み込みサポート) と System 7.x (“デスクトップ・プリンタ機能拡張”をインストール) です。ユーザがセレクタを使って新しいプリンタを選ぶと必ず、新たに選択されたデフォルトプリンタを表すデスクトッププリンタ (DTP) が作成されます。これが完了すれば、ドキュメントを DTP 上にドラッグ&ドロップするだけで、プリントジョブを開始することができます。

デベロッパは自分でプログラムを書いて (セレクタやデスクトッププリンタユーティリティのように) 異なるタイプのデスクトッププリンタを作成することができます。プログラムがデスクトッププリンタを作成してしまえば、オペレーティングシステム (特に“デスクトップ・プリンタ機能拡張”) はそれをセレクタが作成したものと同等に扱います。

各デスクトッププリンタは特定のプリンタドライバと関連付けられています。デスクトッププリンタは現在、StyleWriter 用と LaserWriter 用を作成することができます。Mac OS の将来のバージョンでは、サードパーティのプリンタドライバデベロッパがデスクトッププリンティングを利用できるようにする予定です。

背景

セレクタのコントロールパネルでユーザがプリンタを選択すると、そのプリンタがシステム全体のデフォルトプリンタドライバになります。選択したプリンタドライバのファイルスペックとファイル名は“System”ファイルに記録されます。特に、選択したプリンタドライバの AliasRecord が、リソースタイプ 'alis'、ID -8192 として保存されます。プリンタドライバの名前はリソースタイプ 'STR '、ID -8192 として保存されます。

選択されたプリンタドライバのタイプが LaserWriter だと、対象のプリンタを識別するためにもうひとつの情報が必要です。この情報はプリンタドライバ内にリソースタイプ 'PAPA'、ID -8192 として保存されています。セレクタで AppleTalk ネットワーク上の別の物理プリンタが選択されると必ず、変更を反映するため 'PAPA' リソースが更新されます。

“デスクトップ・プリンタ機能拡張”にはたくさんの役割がありますが、そのひとつは上に記載したリソースの変更を監視することです。新しく選択されたデフォルトプリンタを表すリソースが変更され、それに対応するデスクトッププリンタがないと、“デスクトップ・プリンタ機能拡張”は、自動的にそのプリンタを表す新しいデスクトッププリンタを作成します。

したがって、デスクトッププリンタを作成するために必要な作業は、上の 3 つのリソースを変更することだけです。

警告: “System”ファイルのリソース 'STR ' -8192 の内容と LaserWriter ドライバの 'PAPA' を変更する際、長さは変更してはいけません。変更してしまうと問題が起こります (「Technote 1115: 拡張 'PAPA' リソース」を参照してください)。

注意: 以下のサンプルコードでは Metrowerks PowerPlant クラスがいくつか使用されています。これについては Metrowerks PowerPlant のドキュメントを参照してください。サンプルコードでは C および C++の両方のコーディングスタイルが使われています。サンプルコードはあくまで説明のためのものです。

 

環境のチェック

プログラムの実行中にデスクトッププリンタを作成する場合は、その前に通常の環境チェックに加えて、次のチェックを行ってください。

デスクトップ・プリンタ機能拡張

“デスクトップ・プリンタ機能拡張”はどのようなデスクトッププリンティングにも必要です。System 8.0 以降、この機能は OS に組み込まれています。しかし、System 7.x の場合、これはアドオンの機能拡張で、“機能拡張”フォルダに入っています。“デスクトップ・プリンタ機能拡張”があるかどうかをチェックするコードは次の通りです。

 

// ------------------------------------------------------
// “デスクトップ・プリンタ機能拡張”のチェック
// ------------------------------------------------------
enum { kGestaltPFEFeatures = 'dtpf' };
Boolean HasDesktopExtension()
{
 OSErr err;
 Boolean hasIt = false;
 long gestaltResponse = NULL;
 
 err = Gestalt(kGestaltPFEFeatures, &gestaltResponse);
 if(err == noErr)
  hasIt = true;
 return hasIt;
}

Finder スクリプティング機能拡張

プリンタドライバと“デスクトップ・プリンタ機能拡張”間の通信にはアップルイベントが使用されます。そのため、“Finder スクリプティング機能拡張”がインストールされていることを確認する必要があります。

 

// ------------------------------------------------------
// “Finder スクリプティング機能拡張”のチェック
// ------------------------------------------------------
Boolean HasFinderScripting()
{
long gestaltResponse = NULL;
return ( (Gestalt(gestaltFinderAttr, &gestaltResponse ) == noErr)
&& ((response & (1L << gestaltOSLCompliantFinder)) != 0) );
}

有効なプリンタドライバ

各デスクトッププリンタには有効なプリンタドライバがインストールされていなければなりません。デスクトッププリンタを作成したいプリンタのプリンタドライバがシステムにインストールされていることを確認する必要があります。次に確認するためのコードを示します。プリンタドライバのファイルタイプとクリエータコードは次のとおりだとします。

ファイル

タイプ

クリエータ

LaserWriter 8

'PRER'

'vgrd'

StyleWriter 1200

'PRER'

'dblo'

StyleWriter 2500

'PRER'

'auro'

// --------------------------------------------------------------
// “システム機能拡張”フォルダ内で指定された最小バージョン、タイプ、
// クリエータのプリンタドライバを見つけます。
// 注意: このルーチンはエラーコードを返しません。エラーが起こった場合、
// 呼び出し側に C++例外が返されます。
// 呼び出し側は eofErr を含むエラーを処理しなければなりません。
// eofErr はプリンタドライバが見つからなかったことを示します。
// パラメータは次の通りです。
// inMinVersion - プリンタドライバの最小バージョンを指定
// inFileType - プリンタドライバのファイルタイプを指定
// inCreator - プリンタドライバのクリエータを指定
// outPrinterDrvrFsSpec - プリンタドライバの FSSpec が見つかった場合、
// それを返す
// --------------------------------------------------------------
void FindPrinterDriver(unsigned short inMinVersion, OSType inFileType, OSType inCreator
		void FSSpec& outPrinterDrvrFsSpec )
{
	OSErr err;
	const long kSearchBufSize = 16 * 1024;
	short savedResFile = CurResFile();
	short startupVRefNum;
	long extDirID; // 拡張機能フォルダのID
	CInfoPBRec targetPb;
	CInfoPBRec maskPb;
	CSParam catSearchPb;
	FSSpec foundFsSpec;
	StPointerBlock searchBuf(kSearchBufSize); // PBCatSearch のバッファ
	Boolean foundOne = false;
	 
	err = FindFolder(-1, kExtensionFolderType, FALSE, &startupVRefNum, &extDirID);
	ThrowIfOSErr_(err);
	 
	targetPb.hFileInfo.ioFlFndrInfo.fdType = inFileType;
	targetPb.hFileInfo.ioFlFndrInfo.fdCreator = inCreator;
	targetPb.hFileInfo.ioNamePtr = 0;
	targetPb.hFileInfo.ioFlAttrib = 0x00; // ファイルのみ検索
	targetPb.hFileInfo.ioFlParID = extDirID; // 機能拡張フォルダのみ
	 
	maskPb.hFileInfo.ioFlFndrInfo.fdType = 0xFFFFFFFF;
	maskPb.hFileInfo.ioFlFndrInfo.fdCreator = 0xFFFFFFFF;
	maskPb.hFileInfo.ioFlFndrInfo.fdFlags = 0;
	maskPb.hFileInfo.ioFlFndrInfo.fdLocation.h = 0;
	maskPb.hFileInfo.ioFlFndrInfo.fdLocation.v = 0;
	maskPb.hFileInfo.ioFlFndrInfo.fdFldr = 0;
	maskPb.hFileInfo.ioNamePtr = 0;
	maskPb.hFileInfo.ioFlAttrib = 0x10; // ディレクトリビット
	maskPb.hFileInfo.ioFlParID = extDirID; // 機能拡張フォルダのみ
	 
	Try_{
		catSearchPb.ioCatPosition.initialize = 0;
		while(!foundOne ){
			catSearchPb.ioCompletion = 0;
			catSearchPb.ioNamePtr = 0;
			catSearchPb.ioVRefNum = startupVRefNum;
			catSearchPb.ioMatchPtr = &foundFsSpec;
			catSearchPb.ioReqMatchCount = 1;
			catSearchPb.ioSearchBits = fsSBFlAttrib +
			fsSBFlFndrInfo + fsSBFlParID;
			catSearchPb.ioSearchInfo1 = &targetPb;
			catSearchPb.ioSearchInfo2 = &maskPb;
			catSearchPb.ioSearchTime = 0;
			catSearchPb.ioOptBuffer = (Ptr)searchBuf;
			catSearchPb.ioOptBufSize = kSearchBufSize;
			err = PBCatSearchSync(&catSearchPb);
			ThrowIfOSErr_(err);
			 
			// 指定のタイプのクリエータを持つものが見つかった
			LFile thePrDrFile(foundFsSpec);
			Try_{
				thePrDrFile.OpenResourceFork(fsRdPerm);
				// プリンタドライバのバージョン
				StResource versRsrc('vers', 1, true, true);
				// 最小バージョンと比較
				if(**(unsigned short **)(Handle)versRsrc >= inMinVersion){
					// バージョンの条件は合致
					outPrinterDrvrFsSpec = foundFsSpec;
					foundOne = true; // ループを抜ける
				}
				thePrDrFile.CloseResourceFork();
			}Catch_(inErr){
				thePrDrFile.CloseResourceFork();
				// throw しない
			}EndCatch_
		} // while
	}Catch_(inErr){
		// デフォルトリソースファイルに戻す
		UseResFile(savedResFile);
		Throw_(inErr);
	}EndCatch_
}
 

 

アップルプリンタ用デスクトッププリンタの作成

StyleWriter

このセクションでは、LaserWriter 以外のプリンタタイプのプリンタドライバ、例えば、StyleWriter のプリンタドライバ用の DTP を作成する方法を紹介します。

// ---------------------------------------------------------------------------
// SetResource
// このルーチンは既存のリソースの内容を、inNewContentH が指す新しい
// 内容に変更します。このルーチンが呼び出される前に、ターゲットの
// リソースの入ったリソースファイルを開いて、現在のリソースファイルに
// 設定しなければなりません。
// ---------------------------------------------------------------------------
void
SetResource(ResType inResType, ResID inResID, Handle inNewContentH)
{
	// 変更するリソースを取り出す
	oldResH = Get1Resource(inResType, inResID);
	// リソースがない場合 (oldResH == 0) はエラー処理をすること
	 
	char savedInHdlState = HGetState(inNewContentH);
	char savedRsrcHdlState = HGetState(oldResH );
	Size sz = GetHandleSize(inNewContentH);
	 
	HUnlock(oldResH );
	SetHandleSize(oldResH, sz); // サイズを合わせる
	ThrowIfMemError_();
	HLock(oldResH );
	HLock(inNewContentH);
	BlockMove(*inNewContentH, *oldResH, sz); // 内容をコピー
	 
	HSetState(inNewContentH, savedInHdlState);
	HSetState(oldResH , savedRsrcHdlState);
	ChangedResource(oldResH); // 変更があったことを示す
	WriteResource(oldResH);
	ThrowIfResError_();
	UpdateResFile(CurResFile());
}
// ---------------------------------------------------------------------------
// SetStrResource
// このルーチンは既存の 'STR ' リソースの内容を inStrP が指す、新しい内容に
// 変更します。ターゲットのリソースが入っているリソースファイルを、この
// ルーチンが呼び出される前に開いて、現在のリソースファイルに設定しなけ
// ればなりません。既存のリソースが小さくなるのを防ぐ方法に特に注意して
// 見てください。
// ---------------------------------------------------------------------------
void
SetStrResource(ResType inResType, ResID inResID, ConstStringPtr inStrP )
{
	// 変更するリソースを取り出す
	oldResH = Get1Resource(inResType, inResID);
	// リソースがない場合 (oldResH == 0) はエラー処理をすること
	 
	Size sz = *inStrP + 1;
	/* 文字列がハンドルより長ければ、ハンドルを大きくする */
	if(sz > GetHandleSize(oldResH )){
		char savedRsrcHdlState = HGetState(oldResH );
		HUnlock(oldResH );
		SetHandleSize(oldResH , sz);
		ThrowIfMemError_();
		HSetState(oldResH , savedRsrcHdlState);
	}
	BlockMoveData(inStrP, *oldResH , sz);
	ChangedResource(oldResH );
	WriteResource(oldResH);
	ThrowIfResError_();
	UpdateResFile(CurResFile());
}

“デスクトップ・プリンタ機能拡張”は“Finder 機能拡張”として実装されているため、通信を行うためには Finder に依頼しなければなりません。次のルーチンはこの目的を達成するために必要なものです。

// ---------------------------------------------------------------------------
// アップルイベントを送りたい Finder のアドレスを作成します。
// ここでの「Finder」は最も一般的な意味で使っていることに注意してください。
// 実際には、Finder と AtEase を意味します。
// ---------------------------------------------------------------------------
OSStatus getFinderAddress(AEAddressDesc *theDesc)
{
	OSErr result = noErr;
	ProcessInfoRec processInfo;
	ProcessSerialNumber serialNumber;
	 
	serialNumber.highLongOfPSN = 0;
	serialNumber.lowLongOfPSN = kNoProcess;
	while ((result = GetNextProcess(&serialNumber)) == noErr)
	{
		processInfo.processInfoLength = sizeof(ProcessInfoRec);
		processInfo.processName = nil;
		processInfo.processAppSpec = nil;
		 
		result = GetProcessInformation(&serialNumber, &processInfo);
		if (result == noErr){
			if ( processInfo.processType == 'FNDR' ){
				result = AECreateDesc(typeProcessSerialNumber, (Ptr)&serialNumber,
				sizeof(ProcessSerialNumber), theDesc);
				return(result);
			}
		}
	}
	return(result);
}
 
// ---------------------------------------------------------------------------
// アップルイベントを Finder に送ります。イベントデータは 'dataPtr' が指し、
// その長さは 'dataSize' バイトです。データは Finder が内容を解釈できるよう
// データにはサブタイプを与えます。
// ---------------------------------------------------------------------------
#define kFinderExtension 'fext'
static OSStatus sendAEToFinder( Ptr dataPtr, Size dataSize)
{
	OSStatus err = noErr;
	// アップルイベントデスクリプタに Finder のアドレスを指定
	AEAddressDesc finderAddr;
	AppleEvent theEvent;
	AppleEvent replyEvent;
	 
	err = getFinderAddress(&finderAddr);
	if (err == noErr) {
		err = AECreateAppleEvent(
			kCoreEventClass, // コアイベントを作成
			kFinderExtension, // Finder 機能拡張のイベント
			&finderAddr, // これを Finder に送る
			kAutoGenerateReturnID, // 返り値は取得しない
			kAnyTransactionID, // トランザクション番号は無視
			&theEvent); // ここにすぐ作成
		if (err == noErr)
		{
			err = AEPutParamPtr(
				&theEvent, // 渡すイベント
				keyDirectObject, // ダイレクトオブジェクトキーワード
				kFinderExtension, // Finder 拡張機能に送る
				dataPtr, // これがデータ
				dataSize); // これがその長さ
		 
			if (err == noErr)
			{
				err = AESend(
					&theEvent, // ステータスイベントを送る
					&replyEvent, // リプライイベントはなし、なぜなら...
					kAENoReply + // リプライは不要、
					kAECanInteract + // 受信者はユーザと対話可能で、
					kAEDontReconnect, // エラーが発生しても再接続しないから。
					kAENormalPriority, // 通常イベント
					kAEDefaultTimeout, // 一定時間待つ
					nil, nil); // 動作の結果は無視
			}
			 
			// ここでアップルイベントを解放
			(void) AEDisposeDesc(&theEvent);
		}
	 
		// ここで Finder アドレスを解放
		(void) AEDisposeDesc(&finderAddr);
	}
	 
	// ここまで来る。エラーを (あれば) 返す。
	return(err);
}

“デスクトップ・プリンタ機能拡張”は新しく選択された現在のプリンタを自動的に検出できますが、これは空き時間に関連リソースをチェックすることによって行います。これに若干の時間がかかる場合があります。検出までの時間を短縮するには、次のルーチンで“デスクトップ・プリンタ機能拡張”にアップルイベントを送り、新しいプリンタの追加があったことを通知するとよいでしょう。

// ---------------------------------------------------------------------------
// nessieCreateDTP
// “デスクトップ・プリンタ機能拡張”にアップルイベントを送り、
// 現在のプリンタに対応する新しい DTP の作成を依頼します。
// ---------------------------------------------------------------------------
#define kPrintingExtension 'dtpx'
#define aePFECreateDTP 'pfcr'
typedef struct
{
	OSType pfeCreator;
	OSType extensionType;
} CreateDTPEvent;
 
// ---------------------------------------------------------------------------
// “デスクトップ・プリンタ機能拡張”/Finder に現在のプリンタが変更に
// なったことを通知します
// ---------------------------------------------------------------------------
OSStatus nessieCreateDTP()
{
	OSStatus err;
	CreateDTPEvent myEvent;
	 
	myEvent.pfeCreator = kPrintingExtension;
	myEvent.extensionType = aePFECreateDTP;
	// “デスクトップ・プリンタ機能拡張”は Finder 機能拡張であるため
	err = sendAEToFinder((Ptr) &myEvent, sizeof(myEvent));
	return err;
}

現在のプリンタを指定するリソースを正しく設定し、“デスクトップ・プリンタ機能拡張”への通知が行った後、さらに DTP が実際に作成されたかどうかを調べなければなりません。このため、DTPInfo データ構造体を使用します。

typedef struct DTPInfo{
	short vRefNum; // DTP フォルダの vRefNum
	long dirID; // DTP フォルダのディレクトリ ID
	Str31 DTPName; // DTP フォルダの名前
	OSType driverType; // この DTP のプリンタドライバのクリエータタイプ
	Boolean current; // この DTP は現在デフォルトプリンタかどうか
	Str32 printerName; // ネット上の実際のプリンタ名 (LW8.4 のみ)
	Str32 zoneName; // プリンタのあるゾーン (LW8.4 のみ)
} DTPInfo, *DTPInfoPtr, **DTPInfoHdl;

DTP についての情報を取得する方法は、“デスクトップ・プリンタ機能拡張”への Gestalt 呼び出しを行うことです。Gestalt 呼び出しは DTPInfo のリストを返します。リスト内の各 DTPInfo レコードはそれぞれ 1 個の DTP を記述します。リストを辿って個々の DTPInfo の内容を調べ、希望の DTP が作成されているかを調べることができます。次のルーチンはその方法を説明したものです。

#define errDTPNotFound -1
#define kGestaltPFEInfo 'dtpx'
enum
{
  kOldPFEGestaltStructVersion = 0x02008000, // バージョン 2.0f0 (Mac OS 8.0、8.1、7.x)
  kPFEGestaltStructVersion = 0x03000000 // バージョン 3.0 (Allegro)
};
// GestaltDTPInfo
typedef struct
{
  short vRefNum; // DTP フォルダの vRefNum
  long dirID; // DTP フォルダのディレクトリ ID
  Str31 dtpName; // DTP フォルダ
  OSType driverType; // この DTP のプリンタドライバのクリエータタイプ
  Boolean current; // この DTP は現在デフォルトプリンタか
  Str32 printerName; // ネット上の実際のプリンタの名前 (LaserWriter 8 DTP のみ)
  Str32 zoneName; // このプリンタのあるゾーン (LaserWriter 8 DTP のみ)
} GestaltDTPInfo, *GestaltDTPInfoPtr;
// data associated with the pfe gestalt
typedef struct
{
  long version; // kPFEGestaltStructVersion
  short numDTPs; // アクティブな DTP の個数
  Handle theDTPList; // アクティブな DTP の GestaltDTPInfo リストへのハンドル
  Handle theDTPDriverList; // ドライバのファイルスペックのリストへのハンドル
  Handle theDTPMoreInfoList; // アップルの内部使用のみ
} GestaltPFEInfo, **GestaltPFEInfoHdl;
 
// ---------------------------------------------------------------------------
// DTP のリストを辿り、ネットワークアドレスが 'papaH'、
// ファイルスペックが *printerDrvrFsSpecP のプリンタドライバにあたる
// プリンタを記述する DTP を探します。
// papaH は NULL だと無視します。
// printerDrvrFsSpecP は NULL だと無視します。
//
// 一致するものが見つかったら、noErr を返します。
// 一致するものが見つからない場合は、エラーを返します。
// 呼び出し側が一致したプリンタについての情報がほしい場合は、
// *'DTPInfoP' には NULL 以外の値、つまり DTPInfo 構造体を割り当てて
// その領域を指すポインタを渡してください。
// ---------------------------------------------------------------------------
OSStatus
nessieFindDTP(Handle papaH, FSSpec *printerDrvrFsSpecP, DTPInfo *DTPInfoP)
{
  DTPInfoPtr found = NULL;
  GestaltPFEInfoHdl gestaltResponse;
  char papaHState;
  OSStatus err;
   
  if(papaH){
    papaHState = HGetState(papaH);
    HLock(papaH);
  }
   
  err = Gestalt(kGestaltPFEInfo, (long *)&gestaltResponse);
  if(!err && gestaltResponse != NULL){
    int numDTPs = (*gestaltResponse)->numDTPs;
    DTPInfoHdl DTPH = (*gestaltResponse)->theDTPList;
    FSSpec** driverH = (*gestaltResponse)->theDTPDriverList;
     
    err = errDTPNotFound;
    if((numDTPs > 0) && (DTPH != NULL) && (driverH != NULL)){
      DTPInfoPtr DTP;
      FSSpec *driverP;
      char DTPState = HGetState((Handle)DTPH);
      char driverListState = HGetState((Handle)driverH);
      HLock((Handle)DTPH);
      HLock((Handle)driverH);
      DTP = *DTPH;
      driverP = *driverH;
      if((DTP != NULL) && (driverP != NULL)){
        StringPtr printerStr; // プリンタ名へのポインタ
        StringPtr zoneStr; // ゾーン名へのポインタ
        // (最初はオブジェクトタイプを除外)
        int i;
        if(papaH){
          printerStr = (StringPtr)*papaH; // プリンタ名へのポインタ
          zoneStr = printerStr + *printerStr + 1; // ゾーン名へのポインタ
          // (最初はオブジェクトタイプを除外)
          zoneStr += *zoneStr + 1; // ゾーン名を設定
        }
       
        for(i = 0; i < numDTPs && found == NULL;
          ++i, ++DTP, ++driverP){
          // 探している DTP は、指定のプリンタドライバで作成されており、
          // ゾーン名とプリンタ名が合致するもの
          if(papaH){
            if(((printerDrvrFsSpecP == NULL) ||
            ((printerDrvrFsSpecP->vRefNum == driverP->vRefNum) &&
            (printerDrvrFsSpecP->parID == driverP->parID) &&
            (EqualString(printerDrvrFsSpecP->name, driverP->name, false, false)))) &&
             
            EqualString(printerStr, DTP->printerName, false, false) &&
            EqualString(zoneStr, DTP->zoneName, false, false)){
              found = DTP;
            }
          }else{
            if((printerDrvrFsSpecP != NULL) &&
            ((printerDrvrFsSpecP->vRefNum == driverP->vRefNum) &&
             
            (printerDrvrFsSpecP->parID == driverP->parID) &&
            (EqualString(printerDrvrFsSpecP->name, driverP->name, false, false)))){
              found = DTP;
            }
          }
        }
        if(found != NULL){
          if(DTPInfoP) *DTPInfoP = *found;
        }
      }
      HSetState((Handle)driverH, driverListState);
      HSetState((Handle)DTPH, DTPState);
    }
  }
   
  if(papaH)
    HSetState(papaH, papaHState);
   
  return found == NULL ? errDTPNotFound : noErr;
}
 
// ---------------------------------------------------------------------------
// このルーチンは、プリンタドライバの FSSpec を受け取り、
// 該当する DTP が存在するかどうかを調べます。
// ---------------------------------------------------------------------------
Boolean HasDTP(DTPInfo *DTPInfoP, FSSpec *prDriverFsSpecP)
{
  OSStatus err;
   
  err = nessieFindDTP(NULL, prDriverFsSpecP, DTPInfoP);
  return(!err);
}

これまでに紹介したルーチンの助けを借りて、これから最初の DTP を作成します。このサンプルが示すのは、LaserWriter 以外のタイプのプリンタドライバ用 DTP の作成方法です。LaserWriter (LW) DTP の場合は、LaserWriterExtra() というルーチンを呼び出す必要があり、ここではコメントにして外してあります。(詳細については「LaserWriter」のセクションを参照してください。)

環境の確認後、プリンタドライバの場所を調べます。それから、“System”ファイル内の現在のプリンタドライバを指定する 2 つのリソースを変更し、“デスクトップ・プリンタ機能拡張”にその変更を通知し、新しい DTP が作成されるのを待ちます。待ちループでの動作に注目してください。ループ内では、EventAvail または WaitNextEvent で Finder に制御を渡す必要があります。これをしないと“デスクトップ・プリンタ機能拡張”/Finder は DTP を作成する機会を持つことができません。

#define kWaitForDTPTime (60 * 20)
// ---------------------------------------------------------------------------
// CreateDTP
// このルーチンは StyleWriter1200 用 DTP の作成方法を示します。
// ---------------------------------------------------------------------------
void
CreateDTP()
{
  OSErr err;
  short savedResFile = CurResFile();
  FSSpec printerDriverFSSpec;
  AliasHandle theAlias;
  EventRecord eventRec;
  unsigned long endTime = TickCount() + kWaitForDTPTime;
  DTPInfo DTPInfo;
   
  // 環境のチェック。
  // “デスクトップ・プリンタ機能拡張”および
  // “Finder スクリプティング機能拡張”があることを確認
  if(HasDesktopExtension() && HasFinderScripting()){
    Try_{
      FindPrinterDriver(0x0120,
      'PRER', 'dblo', FSSpec& // StyleWriter1200
      printerDriverFSSpec);
       
      /* LaserWriter タイプのプリンタドライバの DTP を作成するには、
      ここにコードが書く必要がある。
      その場合は次を呼び出す → LaserWriterExtra();
      */
       
      UseResFile(0); // システムファイル
      // プリンタドライバのエイリアスを作成
      err = NewAlias(nil, &printerDriverFSSpec, &theAlias);
      ThrowIfOSErr_(err);
      // 現在のプリンタを該当のプリンタドライバに設定
      SetResource('alis', -8192, theAlias);
      SetStrResource('STR ', -8192, printerDriverFSSpec.name);
      DisposeHandle(theAlias);
       
      // “デスクトップ・プリンタ機能拡張”にデフォルト
      // プリンタの変更を通知
      err = nessieCreateDTP();
      // DTP が作成されたかどうか確認
      do{
        // “デスクトップ・プリンタ機能拡張”に
        // DTP 作成要求を処理する機会を与える
        EventAvail(everyEvent, &eventRec);
        newDTPMade = HasDTP(&DTPInfo, &printerDriverFSSpec);
      }while((TickCount() < endTime) && (newDTPMade == FALSE));
      // DTP が作成されるかタイムアウトするまで
    }Catch_(inErr){
      // エラー処理
    }EndCatch_
  }
  UseResFile(savedResFile);
}
 

LaserWriter 用デスクトッププリンタの作成

LaserWriter 用の DTP を作成するコードを書く場合は、CreateDTP() ルーチンに少し変更を加えるだけで大丈夫です。

FindPrinterDriver を呼び出すところで、StyleWriter 用のクリエータの代わりに、LaserWriter のクリエータを渡します。上のサンプルではコメントで除外してあった LaserWriterExtra() ルーチンも呼び出してください。LaserWriterExtra() は、LaserWriter プリンタドライバ内の 'PAPA' -8192 リソースを変更して、新しいプリンタを指すようにします。

LaserWriterExtra() ルーチンは、LaserWriter 8.5.1 とそれより前のバージョンでは異なります。8.5.1 の場合は DTP のタイプによってさらに違いがあります。

 

8.5.1 より前の LaserWriter 用デスクトッププリンタの作成

8.5.1 より前の LaserWriter では、AppleTalk (PAP) DTP しか作成することができません。ドライバがオリジナルの 103 バイト長の 'PAPA' しかサポートしないためです。LaserWriterExtra() ルーチンは次のようになります。

 

// ---------------------------------------------------------------------------
// 8.5.1 より前の LaserWriter 用 LaserWriterExtra
// 現在のプリンタをターゲットにします。
// ---------------------------------------------------------------------------
LaserWriterExtra(FSSpec *printerDrvrFsSpecP,
	ConstStr32Param inZone,
	ConstStr32Param inPrinterName)
{
	OSErr err;
	char papaState;
	short refNum;
	Handle papaH;
	short savedResFile = CurResFile();
	 
	// プリンタドライバのリソースフォークを開く
	err = FSpOpenResFile(printerDrvrFsSpecP, fsWrPerm);
	// エラー処理
	 
	// プリンタドライバから PAPA リソースを取得
	papaH= Get1Resource('PAPA' , -8192);
	// エラー処理
	 
	papaState = HGetState(papaH);
	HLock(papaH);
	// PAPA リソースの内容を変更
	NBPSetEntity(*papaH, (ConstStr32Param)StringPtr(inPrinterName),
	(ConstStr32Param)("\pLaserWriter"),
	(ConstStr32Param)inZone);
	 
	HSetState(papaH, papaState );
	ChangedResource(papaH); // 変更があったことを示す
	WriteResource(papaH); // リソースを更新
	err = FSClose(refNum);
	UseResFile(savedResFile);
}

 

LaserWriter 8.5.1 以降の場合

LaserWriter 8.5.1 は拡張 PAPA (1024 バイト長) をサポートします。拡張 PAPA を使うと、オリジナルの AppleTalk「PAP」プリンタに加えて、異なるタイプの仮想または物理プリンタを表すデスクトッププリンタを作成することができます。異なるタイプの DTP に印刷すると違う結果を生じます。「保留」DTP はスプールファイルを受け付けるだけのプリンタです。「PostScript トランスレータ」DTP は印刷文書を PostScript 文書に変換するものです。「カスタム」DTP は、あらかじめ指定されたアプリケーションを起動し、変換済みの PostScript 文書を開くよう命じるものです。「LPR」DTP は LPR プロトコルを理解するプリンタ/スプーラに印刷を行います。「赤外線」DTP は赤外線通信ポートを持つ LaserWriter に印刷します。拡張 PAPA の詳細については「TECHNOTE 1115: 拡張 'PAPA' リソース」を参照してください。

PrintingLib には、プログラマが内部構造を理解しなくても拡張 PAPA を作成できるルーチンがあります。詳細については「TECHNOTE 1129: プリンタ設定ライブラリ」を参照してください。

LaserWriter ドライバの PAPA リソースを変更して DTP を作成する前に、DTP を完全に記述する追加のパラメータを設定する必要もあります。現在定義されている DTP 用のパラメータの例は、すべてのタイプの DTP で必要な PPD ファイル、LPR DTP のドメインアドレス、トランスレータ DTP の格納先フォルダ、カスタム DTP の後処理アプリケーションがあります。

パラメータは次の表の通りです。

Collection Tag

Tag ID

DTP Type

parameter

'pppd'

1

すべて

解析済み PPD FSSpec

'ppd '

1

すべて

追加の PPD 仕様

'LpIa'

1

LPR

プリンタの
ドメインアドレス

'Svap'

1

トランスレータ

「ファイルに保存」
の設定

'Pdka'

1

トランスレータ

格納先フォルダの
エイリアス

'TGap'

1

カスタム

後処理用アプリケーションのエイリアス

'PSlv'

1

カスタム

PostScript レベル

'BNok'

1

カスタム

バイナリデータの使用

'JObt'

1

カスタム

PostScriptのタイプ

'FOnt'

1

カスタム

フォント処理

'CsDs'

1

カスタム

後処理用アプリケーションの説明

これらのパラメータはコレクション内にヒントとして保管され、後に LaserWriter の設定ファイルに保存されます。ヒントはコレクションタグと ID によって識別されます。各パラメータの意味とデータ構造はこれらを使用するセクションで詳しく説明します。

PrintingLib にはこれらのコレクションにアクセスするルーチンがあります。詳細については、設定ライブラリ (SettingsLib) のドキュメントを参照してください。コレクションにアクセスする場合は必ずこれらのルーチンを使用してください。ルーチンのなかにはプリンタドライバの設定ファイルの名前を必要とするものがあります。LaserWriter の場合、設定ファイルの名前は LaserWriter ファイルの 'STR ' リソース、ID -8185 に保存されています。

DTP のコレクションにヒントを追加するルーチンは、次のようになります。

#define kNoCollection -2
OSErr psStorePrinterName(Collection prInfo, Handle papa);
/* コレクション prInfo に正規化した EntityName を保存する */
 
/*
DTP のコレクションにヒントを追加します。
長さが 'size' で 'bufP' の指すメモリブロックを、'tag' と 'id' を持つヒントとして、
'papaH' の指定する PAPA を持つ DTP に追加します。
注意: このルーチンを呼び出す前に、LaserWriter のリソースフォークを開いておかなくてはなりません。
*/
OSErr addHint(Handle papaH, CollectionTag tag, long id, long size, void *bufP){
 
	Handle lwPrefFileNameH;
	char savedHandleState;
	Collection DTPCollection;
	 
	// LW の設定ファイルの名前が必要
	lwPrefFileNameH= GetString(-8185 );
	// エラー処理を忘れないように
	savedHandleState = HGetState(inHandle);
	HLock(lwPrefFileNameH);
	// SettingsLib のルーチンでコレクションを取得し項目を追加する
	DTPCollection= psGetPrefsPrinterInfo((StringPtr)(*lwPrefFileNameH), papaH);
	if(DTPCollection== nil)
		// エラー処理を忘れないように
		err = kNoCollection;
	 
	err = psStorePrinterName(DTPCollection, papaH);
	// エラー処理
	err = AddCollectionItem(DTPCollection, tag, id, size, bufP);
	// エラー処理
	err = psUpdatePrefsPrinterInfo(*lwPrefFileNameH, papaH, DTPCollection);
	DisposeCollection(DTPCollection);
	// 後始末
	HSetState(lwPrefFileNameH, savedHandleState );
	ReleaseResource(lwPrefFileNameH);
	return (err);
}

 

すべてのタイプの DTP に対して PPD ファイルを指定することができます。次のルーチンはその方法です。

OSStatus psGetPPDInfo(FSSpecPtr driver, Handle papaH,
		long structVersion, PrinterPPDInfo *ppdInfo);
/* 'driver' や 'papaH' で指定されたプリンタの PPD 情報を返します。
PPD 情報は *'ppdInfo' に返します。
呼び出し側は使用する PrinterPPDInfo 構造体のバージョンを渡さなければなりません。
これは定数の 'kPPDInfoStructVersion' でなければなりません。
呼び出し側の構造体のバージョンが合わないと 'errWrongStructVersion' を返します。
*/
 
OSStatus psSetPPDInfo(FSSpecPtr driver, Handle papaH,
		long structVersion, PrinterPPDInfo *ppdInfo);
/* PPD ファイルと、'driver' や 'papaH' で指定されたプリンタの
解析済み PPD ファイルを設定します。
PPD ファイルの FFSpec と解析済み PPD ファイルは、汎用の PPD を使うべきかどうかを指示
するフラグとともに、ppdinfo の指す構造体で渡されます。
汎用 PPD フラグが設定されている場合は、解析 PPD の FFSpec だけが有効であれば十分です。
structVersion は呼び出し側が使用している PrinterPPDInfo 構造体のバージョンを示します。
呼び出し側は定数を渡さなければなりません。
*/
 
OSErr ppdGetParseFolder(FSSpecPtr parseFolder);
/*
parseFolder が指す FFSpec に解析済み PPD フォルダの 'vRefNum' と 'parID' を設定します。
クライアントは解析済み PPD ファイル名を FFSpec の 'name' フィールドに設定し、ファイル
マネージャの Open 呼び出しで解析済み PPD を開くことができます。
*'parseFolder' は解析済み PPD フォルダの FFSpec ではないことに注意してください。
*/
 
OSErr ppdParseFile (const FSSpec *ppdFileSpec, short compiledRef, short compiledResFRef,
		PPDParseErr *errInfoP);
/* PPD ファイルおよびすべての include を解析します。
FFSpecPtr は終了時にクローズされます。
PDD ファイルの解析結果はファイル参照の 'compiledRef' で開いたファイルに出力されます。
compiledResFRef が -1 でないと、PPD ファイルのリソースフォークをそこにコピーします。
'errInfoP' が NULL 以外だと、エラー情報があれば、*'errInfoP' に返します。
*/
 
enum PPDPresetSource {
	kPPDSourceUnknown = -1,
	kPPDSourceRSRC = 0,
	kPPDSourceGeneric = 1,
	kPPDSourceCustom = 2
};
 
typedef enum{
	kTriFalse = 0,
	kTriTrue,
	kTriUnknown
} TriState;
 
struct CustPPDInfo {
	Boolean usePPD;
	FSSpec fileSpec;
};
typedef struct CustPPDInfo CustPPDInfo, **CustPPDHandle;
 
typedef struct {
short presetSource; /* enum PPDPresetSource */
	TriState isSetup;
	CustPPDInfo customPPD;
} WhatPPD;
 
typedef struct PrinterPPDInfo{
	long structVersion; // 構造体のバージョンを示す
	Boolean useGenericPPD; // 汎用 PPD を使用する場合は True
	FSSpec ppdFile; // 現在の PPD ファイルの FSSpec
	FSSpec parsedPPDFile; // プリンタの解析済み PPD ファイルの FSSpec
}PrinterPPDInfo;
 
/*
このルーチンは、ppdFsSpec によって指定された ppd ファイルを解析し、
対応するヒントを papaH によって指定された DTP に追加します。
*/
OSErr SetPPD(FSSpec* ppdFsSpecP, Handle papaH, FSSpec* laserWriterFsSpecP)
{
	OSErr err = noErr;
	FSSpec parsedPPD;
	WhatPPD whatPPD;
	short savedResFile = ::CurResFile();
	Boolean isGeneric = false;
	short dataForkRefNum, resourceForkRefNum;
	PrinterPPDInfo ppdInfo;
	 
	// デフォルト
	whatPPD.presetSource = kPPDSourceCustom;
	whatPPD.isSetup = kTriTrue;
	whatPPD.customPPD.usePPD = TRUE;
	whatPPD.customPPD.fileSpec = *ppdFsSpecP;
	 
	// 解析済み PPD の FSSpec の準備
	err = ppdGetParseFolder(&parsedPPD);
	ThrowIfOSErr_(err);
	CopyPStr(ppdFsSpecP->name, parsedPPD.name, sizeof(parsedPPD.name));
	 
	// 解析済みファイルを作成し、リソース/データフォークを開く
	FSpCreateResFile(&parsedPPD, 'vgrd', 'Pref', smSystemScript);
	err = SpOpenDF(&parsedPPD, fsRdWrPerm, &dataForkRefNum);
	resourceForkRefNum = ::FSpOpenResFile(&parsedPPD, fsRdWrPerm);
	// PPD の解析
	err = ppdParseFile (ppdFsSpecP, dataForkRefNum, resourceForkRefNum, NULL);
	// リソース/データフォークを閉じる
	CloseResFile(resourceForkRefNum);
	err = FSClose(dataForkRefNum);
	UseResFile(savedResFile);
	 
	// PPD をプリンタと結合
	err = addHint(papaH, 'pppd', 1, sizeof(parsedPPD), &parsedPPD);
	err = addHint(papaH, 'ppd ', 1, sizeof(whatPPD), &whatPPD);
	// デフォルトヒントを設定
	err = psGetPPDInfo(laserWriterFsSpecP, papaH, 2, &ppdInfo);
	if(!err) err = psSetPPDInfo(laserWriterFsSpecP, papaH, 2, &ppdInfo);
	 
	return (err);
}

 

LaserWriter 用 PAP デスクトッププリンタの作成

PAP DTP 用 LaserWriterExtra ルーチンは次のようになります。

// ---------------------------------------------------------------------------
// LaserWriter 8.5.1 用 LaserWriterExtra
// 現在のプリンタをターゲットにします。
// ---------------------------------------------------------------------------
LaserWriterExtra(FSSpec *printerDrvrFsSpecP,
		ConstStr32Param inZone,
		ConstStr32Param inPrinterName)
{
	OSErr err;
	short refNum;
	Handle papaH;
	short savedResFile = CurResFile();
	SSpec ppdFsSpec;
	 
	// プリンタドライバのリソースフォークを開く
	err = FSpOpenResFile(printerDrvrFsSpecP, fsWrPerm, &refNum);
	// エラー処理
	 
	// プリンタドライバから PAPA リソースを取得
	papaH= Get1Resource('PAPA' , -8192);
	// エラー処理
	 
	// PAPA リソースの内容を変更
	// SettingsLib 仕様書の psSetPapPapa を参照
	err = psSetPapPapa(papaH, inPrinterName,
		(const Byte *)("\pLaserWriter"), inZone, 0);
	// エラー処理
	 
	// PPD ファイルを ppdFsSpec で指定
	err = FSMakeFSSpec(vRefNum,dirID,fileName,&ppdFsSpec);
	err = SetPPD(&ppdFsSpec, papaH, printerDrvrFsSpecP);
	 
	ChangedResource(papaH); // 変更があったことを示す
	WriteResource(papaH); // リソースを更新
	err = FSClose(refNum);
	UseResFile(savedResFile);
}

 

LaserWriter 用 LPR デスクトッププリンタの作成

LPR DTP にはドメインアドレスとキューを指定しなければなりません。これらは 'LpIa' ヒントとして指定します。'LpIa' のフォーマットは、プリンタのドメインアドレス (パスカル文字列) に続けてキューの名前 (パスカル文字列) をつなげた形式です。

LPR DTP 用 LaserWriterExtra ルーチンは次のようになります。

// ---------------------------------------------------------------------------
// LaserWriter 8.5.1 用 LaserWriterExtra
// 現在のプリンタをターゲットにします。
// inTcpAddr は LPR プリンタのネットワークアドレスです。
// このアドレスは、名前の形式でもドット形式でも指定できます。
// 例えば "\plaser.rbi.com" または "\p204.188.109.155" となります。
// inQName は inTcpAddr のスプーラに割り当てられたプリントキューの
// 名前です。inQName が NULL だと、プリンタ/スプーラのデフォルトの
// キューを使用します。
// ---------------------------------------------------------------------------
LaserWriterExtra(FSSpec *printerDrvrFsSpecP,
		ConstStr32Param inTcpAddr,
		ConstStr32Param inPrinterName,
		ConstStr32Param inQName)
{
	OSErr err;
	short refNum;
	Handle papaH;
	Handle ipAddrQueueH;
	short savedResFile = CurResFile();
	 
	// プリンタドライバのリソースフォークを開く
	err = FSpOpenResFile(printerDrvrFsSpecP, fsWrPerm, &refNum);
	// エラー処理
	 
	// プリンタドライバから PAPA リソースを取得
	papaH= Get1Resource('PAPA' , -8192);
	// エラー処理
	 
	// PAPA リソースの内容を変更
	// SettingsLib 仕様書の psSetLprPapa を参照
	err = psSetLprPapa(papaH, inPrinterName, (const Byte *)("\p=LPR"),
		(const Byte *)(*inTcpAddr), (const Byte *)inQName);
		// エラー処理
	 
	// PPD ファイルを ppdFsSpec で指定
	err = FSMakeFSSpec(vRefNum,dirID,fileName,&ppdFsSpec);
	err = SetPPD(&ppdFsSpec, papaH, printerDrvrFsSpecP);
	 
	// プリンタのドメインアドレスとキューを指定
	err = PtrToHand(inTcpAddr, &ipAddrQueueH, inTcpAddr[0] + 1);
	err = PtrAndHand(inQName, ipAddrQueueH, inQName[0] + 1);
	HLock(ipAddrQueueH);
	err = addHint(papaH, 'LpIa', 1, GetHandleSize(ipAddrQueueH), *ipAddrQueueH);
	HUnlock(ipAddrQueueH);
	DisposHandle(ipAddrQueueH);
	 
	ChangedResource(papaH); // 変更があったことを示す
	WriteResource(papaH); // リソースを更新
	err = FSClose(refNum);
	UseResFile(savedResFile);
}

 

LaserWriter 用赤外線デスクトッププリンタの作成

// ---------------------------------------------------------------------------
// LaserWriter 8.5.1 用 LaserWriterExtra
// 現在のプリンタをターゲットにします。
// ---------------------------------------------------------------------------
LaserWriterExtra(FSSpec *printerDrvrFsSpecP,
		ConstStr32Param inPrinterName)
{
	OSErr err;
	short refNum;
	Handle papaH;
	short savedResFile = CurResFile();
	 
	// プリンタドライバのリソースフォークを開く
	err = FSpOpenResFile(printerDrvrFsSpecP, fsWrPerm, &refNum);
	// エラー処理
	 
	// プリンタドライバから PAPA リソースを取得
	papaH= Get1Resource('PAPA' , -8192);
	// エラー処理
	 
	// PAPA リソースの内容を変更
	// SettingsLib仕様書のpsSetInfraredPapaを参照
	err = psSetInfraredPapa(papaH, inPrinterName, ("\p=Ird"));
	// エラー処理
	 
	// PPD ファイルを ppdFsSpec で指定
	err = FSMakeFSSpec(vRefNum,dirID,fileName,&ppdFsSpec);
	err = SetPPD(&ppdFsSpec, papaH, printerDrvrFsSpecP);
	 
	ChangedResource(papaH); // 変更があったことを示す
	WriteResource(papaH); // リソースを更新
	err = FSClose(refNum);
	UseResFile(savedResFile);
}
 

LaserWriter 用保留デスクトッププリンタの作成

// ---------------------------------------------------------------------------
// LaserWriter 8.5.1 用 LaserWriterExtra
// 現在のプリンタをターゲットにします。
// ---------------------------------------------------------------------------
LaserWriterExtra(FSSpec *printerDrvrFsSpecP,
		ConstStr32Param inPrinterName)
{
	OSErr err;
	short refNum;
	Handle papaH;
	short savedResFile = CurResFile();
	 
	// プリンタドライバのリソースフォークを開く
	err = FSpOpenResFile(printerDrvrFsSpecP, fsWrPerm, &refNum);
	// エラー処理
	 
	// プリンタドライバから PAPA リソースを取得
	papaH= Get1Resource('PAPA' , -8192);
	// エラー処理
	 
	// PAPA リソースの内容を変更
	// See SettingsLib Spec for psSetHoldPapa
	err = psSetHoldPapa(papaH, inPrinterName, ("\p=Hld"));
	// エラー処理
	 
	// PPD ファイルを ppdFsSpec で指定
	err = FSMakeFSSpec(vRefNum,dirID,fileName,&ppdFsSpec);
	err = SetPPD(&ppdFsSpec, papaH, printerDrvrFsSpecP);
	 
	ChangedResource(papaH); // 変更があったことを示す
	WriteResource(papaH); // リソースを更新
	err = FSClose(refNum);
	UseResFile(savedResFile);
}
 

LaserWriter 用 PostScript トランスレータデスクトッププリンタの作成

PostScript トランスレータ DTP の場合、変換済のファイルを保存するデフォルトの格納先フォルダを 'Pdka' ヒントとして指定しなければなりません。「ファイルに印刷」することを示すため、'Svap' も指定する必要があります。

struct SaveAsFilePrefs{
	Boolean saveToFile; // ファイルに印刷する場合は True
	Boolean restricted; // ファイルに印刷する場合は True
};
 
// ---------------------------------------------------------------------------
// LaserWriter 8.5.1 用 LaserWriterExtra
// 現在のプリンタをターゲットにします。
// ---------------------------------------------------------------------------
LaserWriterExtra(FSSpec *printerDrvrFsSpecP,
		ConstStr32Param inPrinterName,
		FSSpec* destinationFolderP)
{
	OSErr err;
	short refNum;
	Handle papaH;
	SaveAsFilePrefs saveToFilePref;
	AliasHandle printToDiskAliasH;
	short savedResFile = CurResFile();
	 
	// プリンタドライバのリソースフォークを開く
	err = FSpOpenResFile(printerDrvrFsSpecP, fsWrPerm, &refNum);
	// エラー処理
	 
	// プリンタドライバから PAPA リソースを取得
	papaH= Get1Resource('PAPA' , -8192);
	// エラー処理
	 
	// PAPA リソースの内容を変更
	// SettingsLib 仕様書の psSetFilePapa
	err = psSetFilePapa(papaH, inPrinterName, "\p=Fil");
	// エラー処理
	 
	// PPD ファイルを ppdFsSpec で指定
	err = FSMakeFSSpec(vRefNum,dirID,fileName,&ppdFsSpec);
	err = SetPPD(&ppdFsSpec, papaH, printerDrvrFsSpecP);
	 
	// ディスクヒントへ印刷
	saveToFilePref.saveToFile = TRUE;
	saveToFilePref.restricted = TRUE;
	prefCollection.AddItem('Svap', 1, sizeof(saveToFilePref), &saveToFilePref);
	 
	// ディスクのデフォルトフォルダに印刷するよう設定
	err = NewAliasMinimal(destinationFolderP, &printToDiskAliasH);
	HLock((Handle)printToDiskAliasH);
	err = addHint(papaH, 'Pdka', 1,
	GetHandleSize((Handle)printToDiskAliasH), *printToDiskAliasH);
	HUnlock((Handle)printToDiskAliasH);
	DisposeHandle((Handle)printToDiskAliasH);
	 
	// PAPA リソースの内容を変更
	ChangedResource(papaH); // 変更があったことを示す
	WriteResource(papaH); // リソースを更新
	err = FSClose(refNum);
	UseResFile(savedResFile);
}
 

LaserWriter 用カスタムデスクトッププリンタの作成

デベロッパがカスタム DTP を使用する方法と、アップルのデスクトッププリンタユーティリティを変更して特定の後処理アプリケーションの実行をサポートする方法の詳細については「TECHNOTE 1113: デスクトッププリンタユーティリティのカスタマイズ」を参照してください。

カスタム DTP にはそれぞれ、LaserWriter が起動する後処理アプリケーションを指定する必要があります。後処理アプリケーションは AliasHandle として、'TGap' ヒント内に指定します。

現在サポートされているカスタム DTP のヒントは、'PSlv'、'BNok'、'JObt'、'FOnt' です。これらのヒントの意味とデータ構造体については「TECHNOTE 1113: デスクトッププリンタユーティリティのカスタマイズ」で説明しています。

次のルーチンはこうしたパラメータをカスタム DTP のコレクションに追加する方法を示したものです。FOntHint および CustomAppDesc 構造体の詳細については「TECHNOTE 1113: デスクトッププリンタユーティリティのカスタマイズ」を参照してください。

typedef struct{ // 「カスタム DTP」の説明を参照
	long flag; // kIncludeAllFontsBut または kIncludeAllFontsBut
	unsigned char name[1]; // フォント名
}FOntHint;
 
struct CustomAppDesc{
	OSType appSignature; // 後処理アプリケーションのシグネチャ
	Str255 docType; // 新規メニューの文書リストに出す名前
	Str255 helpText; // lin43 - このタイプの DTP が選択されたときに出す
	Str255 usage; // ウィンドウに出す DTP 利用状況
	Str255 appFileName; // エラーメッセージなどで使われるデフォルトアプリケーションファイル名
	short numOfHintsFollow; // 1 から始まる
	HintRsrcSpec hintRsrc[1]; // 可変長
};
typedef struct CustomAppDesc CustomAppDesc;
typedef struct CustomAppDesc* CustomAppDescPtr;
typedef struct CustomAppDesc** CustomAppDescHdl;
 
/*
このルーチンはカスタム DTP のヒントを DTP を指定する papaH に追加します。
ppApp は後処理アプリケーションの FSSpec を指します。
後処理アプリケーションの AliasHandle の生成法と、'TGap' ヒントとして追加する方法に注目してください。
*/
OSErr addCustomDtpParameters(FSSpec* ppApp, Handle papaH)
{
	OSErr err;
	long psLevel = 1; // PostScript レベル 1
	Byte binaryOK = 0; // false
	char job = 0; // psJobPostScript
	FOntHint fontHint;
	AliasHandle targetAppAliasH;
	CustomAppDesc customAppDesc;
	 
	// PostScript レベル 1
	err = addHint(papaH, 'PSlv', 1, siezof(psLevel ), &psLevel ){
	// バイナリは使用しない
	err = addHint(papaH, 'BNok', 1, siezof(binaryOK ), &binaryOK ){
	// PostScript ジョブ
	err = addHint(papaH, 'JObt', 1, siezof(job ), &job ){
	// includeAllFonts
	fontHint.flag = 1;
	fontHint.name [0] = 0;
	err = addHint(papaH, 'FOnt', 1, siezof(fontHint), &fontHint){
	 
	// ターゲットアプリケーションの場所を保存
	err = ::NewAliasMinimal(ppApp, &targetAppAliasH);
	// エラー処理
	::HLock((Handle)targetAppAliasH);
	err = addHint('TGap', 1,
	GetHandleSize((Handle)targetAppAliasH), *targetAppAliasH);
	::HUnlock((Handle)targetAppAliasH);
	::DisposeHandle((Handle)targetAppAliasH);
	 
	// アップルのデスクトッププリンタユーティリティに DTP を認識してもらい、
	// 開いてもらうためには、'CsDs' ヒントを追加しなければならない
	 
	// 後処理アプリケーションの customAppDesc 構造体のフィールドを設定
	customAppDesc.appSignature = 'xxxx'; // 後処理アプリケーションのシグナチャ
	/* その他のフィールドもすべて
	*/
	// カスタムアプリケーションの説明を保存
	err = addHint(('CsDs', 1, sizeof(customAppDesc), &customAppDesc);
	return err;
}
 
// ---------------------------------------------------------------------------
// LaserWriter 8.5.1 用 LaserWriterExtra
// 現在のプリンタをターゲットにします。
// ---------------------------------------------------------------------------
LaserWriterExtra(FSSpec *printerDrvrFsSpecP,
		ConstStr32Param inPrinterName)
{
	OSErr err;
	FSSpec postProcessAppFsSpec;
	short refNum;
	Handle papaH;
	short savedResFile = CurResFile();
	 
	// プリンタドライバのリソースフォークを開く
	err = FSpOpenResFile(printerDrvrFsSpecP, fsWrPerm, &refNum);
	// エラー処理
	 
	// プリンタドライバから PAPA リソースを取得
	papaH= Get1Resource('PAPA' , -8192);
	// エラー処理
	 
	// SettingsLib 仕様書の psSetCustomPapa を参照
	err = psSetCustomPapa(papaH, inPrinterName, "\p=Cst");
	// エラー処理
	 
	// PPD ファイルを ppdFsSpec で指定
	err = FSMakeFSSpec(vRefNum,dirID,fileName,&ppdFsSpec);
	err = SetPPD(&ppdFsSpec, papaH, printerDrvrFsSpecP);
	 
	// 後処理アプリケーションと他のパラメータを指定
	err = FSMakeFSSpec(vRefNum, dirID, "\pYourPostProcessAppName", &postProcessAppFsSpec);
	err = addCustomDtpParameters(&postProcessAppFsSpec, papaH);
	 
	// PAPA リソースの内容を変更
	ChangedResource(papaH); // 変更があったことを示す
	WriteResource(papaH); // リソースを更新
	err = FSClose(refNum);
	UseResFile(savedResFile);
}
 

まとめ

この TECHNOTE で概説したように、プログラムの実行中にデスクトッププリンタを作成するにはちょっとした作業が必要です。しかし、ここで示した注意を守って行えば、多くのアプリケーションにとって、価値ある作業となるはずです。がんばってください。

 

参考文献