pine のすべての投稿

Mac OS X での印刷処理

これはドキュメントタイプでは無いアプリケーションで印刷を行おうと戦った結果です。間違っている所は多々あると思いますが、参考になれば幸いです。
まず、Mac OS X での印刷は印刷するビューを作成してそれを NSPrintOperation で指定すれば可能です。言い換えれば印刷するビューを作成しないと印刷出来ない、と言う事になります。
プログラムでは NSView のサブクラスを作成します。「Print」アクションを受け取るとまず現在設定されている用紙の情報を取得します。
NSPrintInfo *pinfo = [NSPrintInfo sharedPrintInfo];
NSSize paperSize = [pinfo paperSize];
で、印刷するビューを作成します。
PrintView *view;
vew = [[PrintView allocWithZone:[self zone]]
initWithFrame:NSMakeRect(
0.0, 0.0, paperSize.width, paperSize.height)
memos:item];
私はメモを印刷するのでここでメモの情報をセットしています。別に後でも良いかも。
印刷するフォントを設定します。
[view setFont:mTextFont];
印刷する対象が既にフォーマットに関する情報を持っているのなら不要だと思います。私の場合素のテキストなので既定のフォントをセットしています。
NSPrintOperation *op = [NSPrintOperation
printOperationWithView:view
printInfo:pinfo];
[op setShowPanels:YES];
[op runOperation];
これで印刷パネルが表示されます。
その後ビューをリリースします。
[view release];
これで本体側の処理は終わりです。

次に印刷されるビューに関して。
最初は生成時に呼ばれる initWithFrame です。
– (id)initWithFrame:(NSRect)frame memos:(ListItem*)ListItem;
ここでは、まず NSView のフレームのセットと印刷する内容を保持する NSTextStorage レイアウトを担当する NSLayoutManager 実際の表示を担当する NSTextContainer をそれぞれ初期化します。
self = [super initWithFrame:frame];
まず NSView のフレームをセットします。 NSView のサブクラスなので親に対してセットします。
次にNSTextStorage を生成します。
mTextStorage = [[NSTextStorage alloc] init];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
NSLayoutManager を生成します。
[layoutManager setDelegate:self];
NSLayoutManager のデリゲートに自分をセットします。
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
NSTextContainer を生成します。
NSSize tcSize;
tcSize.width = frame.size.width;
tcSize.height = frame.size.height;
NSTextContainer は最初どんな大きさでも表示出来るように可能な最大の大きさを持っていますので、ここでは印刷する1ページの大きさをセットします。
[textContainer setContainerSize:tcSize];
[layoutManager addTextContainer:textContainer];
[textContainer release];
NSTextContainer を NSLayoutManager にセットしリリースします。
[mTextStorage addLayoutManager:layoutManager];
[layoutManager release];
NSLayoutManager を NSTextStorage にセットしてリリースします。
[mTextStorage retain];
NSTextStorage は retain します。drawRect とかで使用するので。

ここで NSTextStorage に印刷するメモの内容をセットしても良いのですが(最初はここでセットしていた)私の場合フォントをセットするので、そのメソッドで行いました。
でもメモのオブジェクトが有るので、それをクラス変数にセットして retain しています。

NSTextStorage に印刷する内容をセットするのですが、NSTextStorage を変更する場合は処理の前に
[mTextStorage beginEditing];
変更が終わった後で
[mTextStorage endEditing];
を実行する必要が有ります。
さらに NSTextStorage にセットする内容は NSAttributedString となります。
特に編集する必要が無い場合は
NSAttributedString *memoAttr = [[NSAttributedString alloc] initWithString:memo];
で大丈夫です。色々とフォントや大きさ、色、等を変更したい場合はここで行って下さい。
[mTextStorage appendAttributedString: memoAttr];
で印刷したい文字列を NSTextStorage にセットします。
ここで印刷したい内容が1ページに収まる場合は特に問題も無く、何もしなくても印刷出来ます。
1ページに収まらない場合は NSLayoutManager に Delegate をセットしたので
– (void)layoutManager:(NSLayoutManager *)aLayoutManager didCompleteLayoutForTextContainer:(NSTextContainer *)aTextContainer atEnd:(BOOL)flag
が呼び出されます。
ここで aTextContainer が nil の場合 NSTextStorage の内容を NSLayoutManager が NSTextContainer に配置していったが一杯になった、と言う事なので initWithFrame でやった様に
NSTextContainer を生成して NSLayoutManager に追加してやります。
NSTextContainer *textContainer = [[NSTextContainer alloc] init];
NSSize tcSize;
tcSize.width = paperSize.width;
tcSize.height = paperSize.height;
[textContainer setContainerSize:tcSize];
[aLayoutManager addTextContainer:textContainer];
[textContainer release];
そして印刷する範囲が広がったのでこの印刷ビューの大きさも大きくします。
NSRect frame = [self frame];
frame.size.height += paperSize.height;
[self setFrame:frame];
で、これが不思議なのですが追加した NSTextContainer のグリフの範囲を取得すると旨く動作します。
NSRange glyphRange = [aLayoutManager glyphRangeForTextContainer:textContainer];
これを実行しないと再度 NSTextContainer が一杯になった時このデリゲートが呼ばれません。
呼ばれないと印刷できない情報が残ってしまいます。
NSLayoutManager のデリゲートにはもう一つ
– (void)layoutManagerDidInvalidateLayout:(NSLayoutManager *)aLayoutManager
が有ります。レイアウトが無効になった時呼び出される、とか。
私は理解出来てませんが、この中では
int glyphNum = [aLayoutManager numberOfGlyphs];
と NSLayoutManager のグリフの数を取得すると旨く動作します。
複数ページ印刷する場合は NSLayoutManager のデリゲートが何度か呼び出されて NSTextContainer を NSLayoutManager に追加する、と言う処理を行います。

実際の1ページの印刷処理は
– (void)drawRect:(NSRect)rect
が担当します。
NSLayoutManager *layoutManager = [[mTextStorage layoutManagers] objectAtIndex:0];
で NSLayoutManager を取得します。今回は NSLayoutManager を1個しか使って無いので NSArray の最初のオブジェクトです。
次に NSTextContainer を取得しますが複数ページの場合 NSTextContainer が複数存在するので印刷対象のページの NSTextContainer を取得します。
NSTextContainer *textContainer = [[layoutManager textContainers] objectAtIndex:page];
取得した NSTextContainer で印刷可能なグリフ範囲を取得します。
NSRange glyphRange = [layoutManager
glyphRangeForTextContainer:textContainer];
で、取得したグリフ範囲を描画します。
[self lockFocus];
[layoutManager drawGlyphsForGlyphRange: glyphRange atPoint: rect.origin];
[self unlockFocus];

以上で、とりあえず印刷は可能です。
今後の課題としては
・デリゲートが追加した NSTextContainer のグリフ範囲を取得しないと続けて呼び出されないのは何故?
・今回は印刷ビューの中でレイアウト処理を行ったが、良いのか?
・先にレイアウトを行って準備出来てから印刷ビューを作成した方が良いのか?

まぁ初めての印刷処理にしては上出来かな、えっ?参考にもならない、すみません。

iPalmMemo 1.1.0 リリース

印刷処理にはまってましたが、なんとか自分が思った様に印刷出来るようになったのでリリースしてしまいまいした。バグ修正も有ったので早い目にリリースしたかったのも有りますが。
別記事で印刷に関して書きたいと思ってます。尤も処理の方法が間違っている可能性が大なのですが、とりあえず印刷できる、と言う事で。参考になればいいなぁ、と。

印刷処理にはまってます

今までのプログラムでは印刷処理を行って無かった、と言うか避けて来たのですが。何を思ったのか iPalmMemoに印刷処理を入れようとしてます。表示されているメモだけの印刷だと現状でもマウスでメモの何処かをクリックして「プリント」を選択すれば印刷出来るのですが(これはシステム側で勝手にやってくれる)。一覧でメモを選択した時はメモだけ、カテゴリを選択した時はそのカテゴリのメモを全部、何も選択されてない時はすべてのメモ。と言う様にしたいのですが。これが結構大変、と言うか印刷処理自体が難しいです。Document タイプのアプリケーションとそうでないアプリケーションでは処理が違うらしいし。ウェブで検索しても只でさえ少ないマックのプログラミング情報なので印刷関係で自分の欲している情報は無かったです。なので色々試行錯誤しながら作成しています。リリースはまだ先になりそう。

QueueMemo ver 1.1

QueueMemo の新しいのをリリースしました。
・サービスメニューからメモを作成出来る様にしました。
・メモを入力している時にファイルをドロップ出来るようにしました。
どちらも対象がテキストファイルだとファイルの内容(テキスト)がメモに挿入されます。テキストファイル以外はファイルを参照するリンクを生成して挿入します。file:// こんな奴です。メモがテキストの場合ブラウザの様にファイル名にリンクの下線が付いて表示されます。クリックすると FInder でダブルクリックした時と同じ動作になります。

QueueMemoをリリースしました

リリースしてしまいました。予告と少し違うのはアクティブなアプリケーションでメモが変わることです。アクティブなアプリケーションでメモを作成すると、作成されたメモはそのアプリケーションがアクティブになれば表示されます。
1秒毎にアクティブなアプリケーションをチェックしているので、ちょっと負荷が高いかも。それとアクティブなアプリケーションを変更した時、メモの切り替わりが遅れた様な感じになります。アクティブなアプリケーションが変わった時に知らせてくれる Notification が有れば良いんですけどね。もしかすると有るかも知れないんですが探せてません。