ホームページ>開発ツール>Xojo / Real Studio Trial and Error・CocoaのDeclareでリッチテキストを扱う・印刷する
Xojo / Real Studio Trial and Error
目次
CocoaのDeclareでリッチテキストを扱う・印刷する
はじめに
以下は、Xojo Cocoaビルドについての話題です。
リッチテキストの印刷を試してみました。
なお検証には、Xojo 2016 Release 3を用いています。(Mac mini mid 2010 + OS X 10.11.6 El Capitan)
注1)印刷機能は、リッチテキストに限定されるものではありませんが、話題の流れ上、ここに置いています。
注2)以下の記事は、手元のプリンタ(キヤノン PIXUS iP4100)で検証したものです。他のプリンタでも同様に動作するかは不明です。
テキストの印刷
Cocoaでの印刷の仕方については、以下の公式ガイドに記載されています。
参考サイト(1):About Printing on the Mac
今回、印刷の対象とするビューは、(ガイドで言及されているNSViewではなく)NSTextViewです。
NSTextViewは、NSViewと違って、折り返しや改ページが文字を意識して行われるため、印刷境界で分断されて文字が読めなくなることがない。単に印刷するだけなら、上記ガイドの例(こちらのListing 3-1。Xojoでの記述は以下の例1で示した。)の通り実装すればよく、簡単です。
ただしこの方法では、(当方の実験結果では)横方向が用紙幅をオーバーした分は印刷されませんでしたので、全て印刷したい場合はビューの幅をウィンドウ上で適切に調整する必要がありました。
一方、画面サイズに関係なく、内容を用紙内に収まるように印刷したい場合は、用紙サイズやマージン(余白)といったパラメータをビューのサイズに反映させる必要が生じます。(この辺の考え方は、Xojoネイティブの印刷と基本的に同じと言っていいと思います。)
WYSIWYGを目指すのであれば、画面用と印刷用のビューを同期させるための工夫が必要になると思われるが、そこまではしない。パラメータの標準値は、NSPrintInfoクラスのsharedPrintInfoに保持されています。(上記例の印刷でも、暗黙の内にこのパラメータが使用されます。)
なので、初回はこれをベースに、必要に応じてカスタマイズすることになろうかと思われます。
次回以降は、カスタマイズした結果を内部で保持することにより、使い回すこともできます。
パラメータのうち、用紙サイズや方向は、「ページ設定」パネルで指定/取得できます。
部数やページの選択は、「プリント」パネルで指定/取得できます。(カスタマイズすれば、こちらで用紙サイズや方向を扱うことも可。)
なお、マージンはプリンタ側からの要請で決まるので、ユーザが指定することはありません。(内部的に変更することは可。)
ヘッダ/フッタを付加したい場合は、これも上記ガイドのこちらのページにあるように、ビュー内に描画するのではなく、別途、drawPageBorderWithSize:メソッドをオーバーライドして、こちらで対応することになります。メソッドのオーバーライドには、お馴染みのObjective-CのランタイムAPIを用いた動的クラス生成を用います。
パラメータのファイル保存は、(プリント用パネルで表示される)「プリセット」とは連動せず、独自に保存しています。(アップルのドキュメントによると、通常のplistでは保存できないからNSKeyedArchiver使えとかあるのですが、プリセットがこの辺とどう関わってくるのか、よく分かっていません。)
また当初は、NSPrintInfoを丸ごと保存/復元しようとしたのですが、うまくいかなかった(注3)ので、dictionaryを取り出して保存、復元時はdictionaryからNSPrintInfoを生成するようにしています。
注3)保存/復元のプロセスは通るが、使おうとすると、そんなメソッドはないとエラーが出る。どうも、復元したものがNSPrintInfo型と認識されていないようなのだが、それ以上は未調査。
モーダルダイアログとシート
ページ設定/プリント用パネルには、モーダルダイアログ(以下、ダイアログ)とシートがあります。
ダイアログとシートには外観の違いだけでなく、動作にも違いがあるようです。
参考サイト(2):NSApplication(モーダルダイアログの項)
参考サイト(3):Introduction to Sheets
ダイアログメソッドとシートメソッドそれぞれの直後にmsgBoxを置いてみると、ダイアログメソッドではダイアログを閉じてから表示されるが、シートメソッドではシート表示後直ちに表示される。この関係で、シートではボタンのクリックをセレクタで処理するようになっていますが、これもObjective-CのランタイムAPIを用いた動的クラス生成で対応します。
ただし、実験の結果では、セレクタは特に指定しなくても、動作に支障はないようです。(ボタンごとの動作やPrintInfoへの反映はシステム側がやってくれる。)
以下に示す例2では、保存可否フラグの設定に使っているが、これも常に保存(または常に保存しない)であれば、必要はない。また、ダイアログ/シートを出すクラスが複数あります。確認できただけでも、
それぞれ試してみましたが、NSPrintPanelのメソッドについては、ビューの指定方法が分からなかったため、以降のサンプルではNSPrintOperationのメソッドを使っています。
種別 クラス名 メソッド名 ページ設定 NSApplication -runPageLayout:(ダイアログ) NSPageLayout -runModal(ダイアログ)
-runModalWithPrintInfo:(ダイアログ)
-beginSheetWithPrintInfo:modalForWindow:delegate:didEndSelector:contextInfo:(シート)プリント NSPrintPanel -runModal(ダイアログ)
-beginSheetWithPrintInfo:modalForWindow:delegate:didEndSelector:contextInfo:(シート)NSPrintOperation -showsPrintPanel(ダイアログ)
-runOperationModalForWindow:delegate:didRunSelector:contextInfo:(シート)
どうも、プリントパネルのプレビュー画面と「プレビューでPDFを開く」ボタンの機能は、ビューを指定することで有効になるようだ。(推測)
<参考>
ページ設定用シートでは、セレクタとして指定したメソッド内では、変更後の値を取得できませんでした。どうもパラメータが書き変わるまでに暫く時間がかかるようで、保存したい場合はタイマーで時間差を設けてやる等の必要がありそうです。おそらく、このタイミングでは保存せずに、ウィンドウクローズ時等に保存するのではないかと思われるのですが、詳細は未調査。
プリント用シートでは、セレクタとして指定したメソッドに渡ってくるリターンコードが、OKボタンとプレビューボタンで同じ値になります(アップルのドキュメントにもあるので仕様のようだ)が、前述の通り、ボタン処理はシステム側がやってくれるので、これで困ることはなさそうです。
サンプルの仕様
以上を踏まえて今回は、印刷の実行のみのシンプルなものと、ページ設定に内容をフィットさせてヘッダ/フッタを付加するものを試してみることにしました。
それぞれの仕様は以下の通りとしました。
例1(シンプル)
例2(ページ設定に内容をフィットさせてヘッダ/フッタを付加)
- リッチテキストのプロジェクトをベースとする
- メニューハンドラを実装して、プリントの処理を記述する
- ページ設定は行わない(標準値が使われる)
- プリント用パネル表示はシステムに任せる(結果として、ダイアログになる)
- リッチテキストのプロジェクトをベースとする
- メニューハンドラを実装して、ページ設定/プリントの処理を記述する
- ページ設定/プリントとも、パネルにはシートを使用する
- ページ設定を反映した印刷用TextViewを使用する
- DrawPageBorderWithSize:メソッドをオーバーライドして、ヘッダ/フッタを描画する
- ヘッダは左に書類名、右に印刷日時、フッタはページ番号とする
- カスタマイズしたPrintInfoをファイルに保存して、次回以降の初期値として使えるようにする
- ページ設定を行わなくても警告(またはページ設定パネル)は出さず、処理を継続する(この場合は、標準値が使われる)
- 印刷用TextVIewとシートのセレクタは、クラス化する
Xojoでの実装(例1)
実行してみたところ、印刷が機能することを確認しました。
- 前回プロジェクトをベースとする
- MainMenuBarのFileMenuにFilePrintを追加
- 以下をWindow1のメニューハンドラ(Menu Handlers)に追加
メニュー項目名: FilePrint // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // TextArea1のDocumentViewを取得 declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* Dim textView As Ptr = documentView(TextArea1.Handle) // PrintOperationの生成 Dim pope As Ptr = NSClassFromString("NSPrintOperation") Declare Function printOperation Lib "Cocoa" selector "printOperationWithView:" (class_id As Ptr, view As Ptr) As Ptr pope = printOperation(pope, textView) // PrintOperationの実行(ダイアログ生成を伴う) Declare Sub runOperation Lib "Cocoa" selector "runOperation" (obj_id As Ptr) runOperation(pope) Return True
Xojoでの実装(例2)
実行してみたところ、印刷が機能することを確認しました。
- 前回プロジェクトをベースとする
- MainMenuBarのFileMenuにFilePagesetupを追加
- MainMenuBarのFileMenuにFilePrintを追加
- 以下をWindow1のメニューハンドラ(Menu Handlers)に追加
メニュー項目名: FilePagesetup PageSetup() Return True
- 以下をWindow1のメニューハンドラ(Menu Handlers)に追加
メニュー項目名: FilePrint PrintContent() Return True
- 以下をWindow1のCancelCloseイベントに追加
// PrintInfoの書き出し SavePinfo()
- 以下をWindow1のOpenイベントに追加(既プロジェクトで記述したコードの後に追加)
// PrintInfoの読み込み OpenPinfo()
- 以下をWindow1のメソッド(Methods)に追加
注)ファイルの場所は、ここではデスクトップとしましたが、必要に応じて変更して下さい。メソッド名: OpenPinfo // プリンタ情報ファイルの取得 Dim f as FolderItem = SpecialFolder.Desktop.Child("pinfo") // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Dim pinfo As Ptr = NSClassFromString("NSPrintInfo") // クラスメソッドなので、まずNSPrintInfoクラスを取得 if f.Exists=false then // ファイルが存在しなければ // 標準のPrintInfoを取得 Declare Function sharedPrintInfo Lib "Cocoa" Selector "sharedPrintInfo" (receiver As Ptr) As Ptr pinfo = sharedPrintInfo(pinfo) else // ファイルからdataを取得。この時、NSDictionaryとして復元される。(NSKeyedUnarchiverを使用) Dim keyUarc As Ptr = NSClassFromString("NSKeyedUnarchiver") // クラスメソッドなので、まずNSKeyedUnarchiverクラスを取得 Declare Function unarchiveObjectWithFile Lib "Cocoa" Selector "unarchiveObjectWithFile:" (receiver As Ptr, path As CFStringRef) As Ptr Dim dict As Ptr = unarchiveObjectWithFile(keyUarc, f.NativePath) // ファイルから読み込み // NSDictionaryからNSPrintInfoを生成 Declare Function alloc Lib "Cocoa" selector "alloc" (receiver As Ptr) As Ptr Declare Function initWithDictionary Lib "Cocoa" selector "initWithDictionary:" (receiver As Ptr, path As Ptr) As Ptr pinfo = initWithDictionary(alloc(pinfo), dict) end if // PrintInfoを保持 PrintSheet.printInfo = pinfo
- 以下をWindow1のメソッド(Methods)に追加
注)didEndSelectorで指定するセレクタの引数は、こちらに準拠しています。メソッド名: PageSetup // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // NSPageLayoutの取得 Dim pnt1 As Ptr = NSClassFromString("NSPageLayout") Declare Function pageLayout Lib "Cocoa" Selector "pageLayout" (receiver As Ptr) As Ptr pnt1 = pageLayout(pnt1) // ページ設定シートを表示 Declare Sub beginSheetWithPrintInfo Lib "Cocoa" Selector "beginSheetWithPrintInfo:modalForWindow:delegate:didEndSelector:contextInfo:" (receiver As Ptr, info As Ptr, win As Integer, dlgt As Ptr, sel As Ptr, cntx As Ptr) beginSheetWithPrintInfo(pnt1, PrintSheet.printInfo, self.Handle, PrintSheet.makeDelegate(), NSSelectorFromString("pageLayoutDidEnd:returnCode:conextInfo:"), nil)
- 以下をWindow1のメソッド(Methods)に追加
メソッド名: PrintContent // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // NSPrintInfoの取得とセット(ポインターの代入だと元のPrintDialog.printInfoが書き換わってしまうので、コピーを作る) Declare Function dictionary Lib "Cocoa" selector "dictionary" (class_id As Ptr) As Ptr Dim dict As Ptr = dictionary(PrintSheet.printInfo) // PrintDialog.printInfoからdictionaryを取得 Dim pinfo As Ptr = NSClassFromString("NSPrintInfo") // クラスメソッドなので、まずNSPrintInfoクラスを取得 Declare Function alloc Lib "Cocoa" selector "alloc" (receiver As Ptr) As Ptr Declare Function initWithDictionary Lib "Cocoa" selector "initWithDictionary:" (receiver As Ptr, path As Ptr) As Ptr pinfo = initWithDictionary(alloc(pinfo), dict) // dictionaryからNSPrintInfoを生成 // パラメータのカスタマイズ Declare Sub setHorizontallyCentered Lib "Cocoa" selector "setHorizontallyCentered:" (obj_id As Ptr, flg As Boolean) setHorizontallyCentered(pinfo, false) // 水平方向のセンタリングを無効にセット Declare Sub setVerticallyCentered Lib "Cocoa" selector "setVerticallyCentered:" (obj_id As Ptr, flg As Boolean) setVerticallyCentered(pinfo, false) // 垂直方向のセンタリングを無効にセット Declare Function paperSize Lib "Cocoa" selector "paperSize" (obj_id As Ptr) As NSSize Dim paperSize As NSSize = paperSize(pinfo) Dim margin As GFMargin Declare Function leftMargin Lib "Cocoa" selector "leftMargin" (obj_id As Ptr) As Single margin.Left = leftMargin(pinfo) // 左マージンの取得 Declare Function topMargin Lib "Cocoa" selector "topMargin" (obj_id As Ptr) As Single margin.Top = topMargin(pinfo) // 上マージンの取得 Declare Function rightMargin Lib "Cocoa" selector "rightMargin" (obj_id As Ptr) As Single margin.Right = rightMargin(pinfo) // 右マージンの取得 Declare Function bottomMargin Lib "Cocoa" selector "bottomMargin" (obj_id As Ptr) As Single margin.Bottom = bottomMargin(pinfo) // 下マージンの取得 // NSTextViewPrintの生成。引数は順に、生成したインスタンスへのポインタ(戻り値)、用紙サイズ、マージン、書類名(ウィンドウのタイトル) Dim textView As Ptr Dim a As NSTextViewPrint = new NSTextViewPrint(textView,paperSize,margin,me.Title) // NSTextViewPrintのインスタンスにTextArea1の内容をセット PrintContent2(textView) // Bottomが上辺の設定になっている Declare Sub setBottomMargin Lib "Cocoa" selector "setBottomMargin:" (obj_id As Ptr, mgn As Single) setBottomMargin(pinfo, margin.Bottom+17) // ヘッダ領域分を差し引く // Topが下辺の設定になっている Declare Sub setTopMargin Lib "Cocoa" selector "setTopMargin:" (obj_id As Ptr, mgn As Single) setTopMargin(pinfo, margin.Top+15) // フッタ領域分を差し引く // PrintOperationの生成 Dim pope As Ptr = NSClassFromString("NSPrintOperation") Declare Function printOperation Lib "Cocoa" selector "printOperationWithView:printInfo:" (class_id As Ptr, view As Ptr, pinfo As Ptr) As Ptr pope = printOperation(pope, textView, pinfo) // PrintOperationの実行(シート生成を伴う。実行をPrintOperation側に任せる場合は、delegate,didRunSelectorは設定不要?っぽい) Declare Sub runOperationModalForWindow Lib "Cocoa" Selector "runOperationModalForWindow:delegate:didRunSelector:contextInfo:" (receiver As Ptr, win As Integer, dlgt As Ptr, sel As Ptr, cntx As Ptr) runOperationModalForWindow(pope, Window1.Handle, nil, nil, nil)
- 以下をWindow1のメソッド(Methods)に追加
メソッド名: PrintContent2 引数: textView As Ptr // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // TextArea1のDocumentViewを取得 declare function documentView lib "Cocoa" selector "documentView" (obj_id as Integer) as Ptr // Return NSTextView* Dim pnt1 As Ptr = documentView(TextArea1.Handle) // ef.Handle = NSScrollView* // TextArea1のNSTextStorageを取得(=NSAttributedStringの取得) declare function textStorage lib "Cocoa" selector "textStorage" (obj_id as Ptr) As Ptr // Return NSTextStorage* Dim attr As Ptr = textStorage(pnt1) // textViewのNSTextStorageを取得 Dim pnt3 As Ptr = textStorage(textView) // ストレージにスタイル付きテキストをセット Declare Sub setAttributedString Lib "Cocoa" Selector "setAttributedString:" (receiver As Ptr, identifier As Ptr) setAttributedString(pnt3, attr)
- 以下をWindow1のメソッド(Methods)に追加
注)ファイルの場所は、ここではデスクトップとしましたが、必要に応じて変更して下さい。メソッド名: SavePinfo // NSPrintInfo保存の操作がされていなければ戻る if PrintSheet.saved=false then return end if // プリンタ情報ファイルの取得 Dim f as FolderItem = SpecialFolder.Desktop.Child("pinfo") // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // NSPrintInfoからdictionaryを取得 Declare Function dictionary Lib "Cocoa" selector "dictionary" (class_id As Ptr) As Ptr Dim dict As Ptr = dictionary(PrintSheet.printInfo) // dictionaryをNSDataに変換(NSKeyedArchiverを使用) Dim keyArc As Ptr = NSClassFromString("NSKeyedArchiver") // クラスメソッドなので、まずNSKeyedArchiverクラスを取得 Declare Function archivedDataWithRootObject Lib "Cocoa" Selector "archivedDataWithRootObject:" (receiver As Ptr, obj As Ptr) As Ptr Dim data As Ptr = archivedDataWithRootObject(keyArc, dict) // Return NSData* // NSDataをファイルに書き出し Declare Sub writeToFile Lib "Cocoa" Selector "writeToFile:atomically:" (receiver As Ptr, path As CFStringRef, flag As Boolean) writeToFile(data, f.NativePath, true) // ファイルに保存
- 新規クラスを作成(名前は、ここでは「NSTextViewPrint」とした。)
- 以下をNSTextViewPrintのメソッド(Methods)に追加(注:Constructorは予約語で、クラスをnewした時に自動的に呼び出される。)
メソッド名: Constructor 引数:byRef inst As Ptr, paperSize As NSSize, margin As GFMargin, title As String // NSTextViewを継承したカスタムクラスを作成。初回のみ makeClass() // 本文エリア(印刷可能領域から、ヘッダ/フッタ領域を差し引いたもの)のRect生成 Dim rect As NSRect = NSMakeRect(0, 0, paperSize.Width-margin.Left-margin.Right, paperSize.Height-margin.Top-15-margin.Bottom-17) // インスタンスを作成 Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function initWithFrame Lib "Cocoa" selector "initWithFrame:" (obj_id As Ptr, frame As NSRect) As Ptr inst = initWithFrame(alloc(NSTextViewClass), rect) // インスタンスを保持 NSTextViewInst = inst XojoInst = self // 外部指定されたパラメータを保持 pPaperSize = paperSize pMargin = margin pTitle = title
- 以下をNSTextViewPrintのメソッド(Methods)に追加
メソッド名: DrawPageBorderWithSize 引数:size As NSSize // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr // ヘッダ(左)用スタイル付きテキスト生成 Dim stHL As Ptr = GetHeaderString(pTitle, 0) // 0 = NSLeftTextAlignment // ヘッダ(右)用スタイル付きテキスト生成 Dim d As new date // 現在日時の取得 Dim stHR As Ptr = GetHeaderString(d.LongDate+" "+d.LongTime, 1) // 1 = NSRightTextAlignment // ヘッダのポジション Dim rectH As NSRect = NSMakeRect(pMargin.Left, pMargin.Top-3, pPaperSize.Width-pMargin.Left-pMargin.Right, 22) // フッタ用スタイル付きテキスト生成 Dim stF As Ptr = GetFooterSrring() // フッタのポジション Dim rectF As NSRect = NSMakeRect(0, pPaperSize.Height-pMargin.Bottom-10, pPaperSize.Width, 22) // ロックフォーカス(これをしないと、文字が上下反転する) Declare Sub lockFocus Lib "Cocoa" selector "lockFocus" (class_id As Ptr) lockFocus(NSTextViewInst) // ヘッダ・フッタの描画 Declare Sub drawInRect Lib "Cocoa" selector "drawInRect:" (class_id As Ptr, rect As NSRect) drawInRect(stHL, rectH) drawInRect(stHR, rectH) drawInRect(stF, rectF) // アンロックフォーカス Declare Sub unlockFocus Lib "Cocoa" selector "unlockFocus" (class_id As Ptr) unlockFocus(NSTextViewInst)
- 以下をNSTextViewPrintのメソッド(Methods)に追加
メソッド名: GetFooterSrring 戻り値型:Ptr // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As Ptr, key As CFStringRef) // アトリビュート用Dictionaryの生成 Dim option As Ptr = NSClassFromString("NSMutableDictionary") Declare Function dictionary Lib "Cocoa" Selector "dictionary" (receiver As Ptr) As Ptr option = dictionary(option) // フォントの設定 Dim font As Ptr = NSClassFromString("NSFont") Declare Function systemFontOfSize Lib "Cocoa" Selector "systemFontOfSize:" (receiver As Ptr, size As Single) As Ptr font = systemFontOfSize(font, 9.0) // フォントをDictionaryにセット setObject(option, font, "NSFont") // ParagraphStyleの生成 Dim pgf As Ptr = NSClassFromString("NSMutableParagraphStyle") Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr pgf = init(alloc(pgf)) // 揃えの設定(ここではセンタリングを指定) Declare Sub setAlignment Lib "Cocoa" Selector "setAlignment:" (receiver As Ptr, type As Integer) setAlignment(pgf, 2) // 2 = NSCenterTextAlignment // ParagraphStyleをDictionaryにセット setObject(option, pgf, "NSParagraphStyle") // 現在印刷中のページ番号を取得 Dim pope As Ptr = NSClassFromString("NSPrintOperation") Declare Function currentOperation Lib "Cocoa" selector "currentOperation" (class_id As Ptr) As Ptr pope = currentOperation(pope) Declare Function currentPage Lib "Cocoa" selector "currentPage" (class_id As Ptr) As Integer Dim page As Integer = currentPage(pope) // スタイル付きテキストを生成 Dim attStr As Ptr = NSClassFromString("NSAttributedString") attStr = alloc(attStr) Declare Function initWithString Lib "Cocoa" Selector "initWithString:attributes:" (receiver As Ptr, str As CFStringRef, attr As Ptr) As Ptr attStr = initWithString(attStr, "- "+Str(page)+" -", option) // スタイル付きテキストを返す return attStr
- 以下をNSTextViewPrintのメソッド(Methods)に追加
メソッド名: GetHeaderString 引数:txt As String, align As Integer 戻り値型:Ptr // 文字列を指定してクラスオブジェクトを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Sub setObject Lib "Cocoa" Selector "setObject:forKey:" (receiver As Ptr, obj As Ptr, key As CFStringRef) // アトリビュート用Dictionaryの生成 Dim option As Ptr = NSClassFromString("NSMutableDictionary") Declare Function dictionary Lib "Cocoa" Selector "dictionary" (receiver As Ptr) As Ptr option = dictionary(option) // フォントの設定 Dim font As Ptr = NSClassFromString("NSFont") Declare Function systemFontOfSize Lib "Cocoa" Selector "systemFontOfSize:" (receiver As Ptr, size As Single) As Ptr font = systemFontOfSize(font, 9.0) // フォントをDictionaryにセット setObject(option, font, "NSFont") // ParagraphStyleの生成 Dim pgf As Ptr = NSClassFromString("NSMutableParagraphStyle") Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr pgf = init(alloc(pgf)) // 揃えの設定(ここでは外部からのパラメータを指定) Declare Sub setAlignment Lib "Cocoa" Selector "setAlignment:" (receiver As Ptr, type As Integer) setAlignment(pgf, align) // ParagraphStyleをDictionaryにセット setObject(option, pgf, "NSParagraphStyle") // スタイル付きテキストを生成 Dim attStr As Ptr = NSClassFromString("NSAttributedString") attStr = alloc(attStr) Declare Function initWithString Lib "Cocoa" Selector "initWithString:attributes:" (receiver As Ptr, str As CFStringRef, attr As Ptr) As Ptr attStr = initWithString(attStr, txt, option) // スタイル付きテキストを返す return attStr
- 以下をNSTextViewPrintのプロパティ(Properties)に追加
プロパティ名: pMargin データ型: GFMargin 標準値: なし
- 以下をNSTextViewPrintのプロパティ(Properties)に追加
プロパティ名: pPaperSize データ型: NSSize 標準値: なし
- 以下をNSTextViewPrintのプロパティ(Properties)に追加
プロパティ名: pTitle データ型: String 標準値: なし
- 以下をNSTextViewPrintの共有メソッド(Shared Methods)に追加
メソッド名: makeClass // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // Declare宣言 Declare Function objc_allocateClassPair Lib "Cocoa" (superclass As Ptr, name As CString, extraBytes As Integer) as Ptr Declare Sub objc_registerClassPair Lib "Cocoa" (cls As Ptr) Declare Function class_addMethod Lib "Cocoa" (cls As Ptr, name As Ptr, imp As Ptr, types As CString) As Boolean // 既にクラス作成済なら戻る if NSTextViewClass <> nil then return end if // クラス名をmyNSTextViewPrint、メタクラス名をNSTextViewにして、生成 Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSTextView"), "myNSTextViewPrint", 0) // ランタイムに登録(参照を可能とするため) objc_registerClassPair newClassId // Delegateの対象となるメソッドを追加(drawPageBorderWithSize:をXojo側で用意したmyDrawPageBorderWithSizeメソッドで受け取る。) if not class_addMethod (newClassId, NSSelectorFromString("drawPageBorderWithSize:"), AddressOf myDrawPageBorderWithSize, "v24@0:8@16") then msgBox "error1." return end if // クラスを保持 NSTextViewClass = newClassId
- 以下をNSTextViewPrintの共有メソッド(Shared Methods)に追加
注)SELの型をPtrとしていたが、CStringの方が相応しいので変更した。(未使用なので変更しなくても実害はなし。)(2018.07.30)メソッド名: myDrawPageBorderWithSize 引数:id As Ptr, SEL As CString, size As NSSize // インスタンスメソッドに渡す XojoInst.DrawPageBorderWithSize(size)
- 以下をNSTextViewPrintの共有プロパティ(Shared Properties)に追加
プロパティ名: NSTextViewClass データ型: Ptr 標準値: なし
- 以下をNSTextViewPrintの共有プロパティ(Shared Properties)に追加
プロパティ名: NSTextViewInst データ型: Ptr 標準値: なし
- 以下をNSTextViewPrintの共有プロパティ(Shared Properties)に追加
プロパティ名: XojoInst データ型: NSTextViewPrint 標準値: なし
- 新規クラスを作成(名前は、ここでは「PrintSheet」とした。)
- 以下をPrintSheetの共有メソッド(Shared Methods)に追加
注)SELの型をPtrとしていたが、CStringの方が相応しいので変更した。(未使用なので変更しなくても実害はなし。)(2018.07.30)メソッド名: pageLayoutDidEnd 引数:id As Ptr, SEL As CString, sender As Ptr, rcode As Integer, info As Ptr // rcode = 0 : Cancel // rcode = 1 : OK if rcode=1 then // 保存されたことを示すフラグ・オン(終了時にファイル保存するかどうかの判定用) saved=true end if
- 以下をPrintSheetの共有メソッド(Shared Methods)に追加
メソッド名: makeDelegate // 既に作成済なら値を返す if delegateId <> nil then return delegateId end if // 文字列を指定してクラスオブジェクト/セレクタを取得する。最初に一回宣言しておけばよい。 Declare Function NSClassFromString Lib "Cocoa" (aClassName As CFStringRef) As Ptr Declare Function NSSelectorFromString Lib "Cocoa" (aSelName As CFStringRef) As Ptr // Declare宣言 Declare Function objc_allocateClassPair Lib "Cocoa" (superclass As Ptr, name As CString, extraBytes As Integer) as Ptr Declare Sub objc_registerClassPair Lib "Cocoa" (cls As Ptr) Declare Function class_addMethod Lib "Cocoa" (cls As Ptr, name As Ptr, imp As Ptr, types As CString) As Boolean // クラス名をmyClass1Delegate(名前は任意。少なくとも今回のケースでは参照されない。)、メタクラス名をNSObjectにして、生成 Dim newClassId As Ptr = objc_allocateClassPair(NSClassFromString("NSObject"), "myClass1Delegate", 0) // ランタイムに登録(参照を可能とするため) objc_registerClassPair newClassId if not class_addMethod (newClassId, NSSelectorFromString("pageLayoutDidEnd:returnCode:conextInfo:"), AddressOf pageLayoutDidEnd, "@@:@") then msgBox "error." return nil end if // 上記で生成したクラスのインスタンスを作成 Declare Function alloc Lib "Cocoa" selector "alloc" (class_id As Ptr) As Ptr Declare Function init Lib "Cocoa" selector "init" (obj_id As Ptr) As Ptr delegateId = init(alloc(newClassId)) // インスタンスを返す return delegateId
- 以下をPrintSheetの共有プロパティ(Shared Properties)に追加
プロパティ名: delegateId データ型: Ptr 標準値: なし
- 以下をPrintSheetの共有プロパティ(Shared Properties)に追加
プロパティ名: printInfo データ型: Ptr 標準値: なし
- 以下をPrintSheetの共有プロパティ(Shared Properties)に追加
プロパティ名: saved データ型: Boolean 標準値: false
- 新規モジュールを作成(名前は、ここでは「NSGlobals」とした。)
- 以下をNSGlobalsの構造体(Structures)に追加
構造体名: GFMargin スコープ:Global Left As Single Top As Single Right As Single Bottom As Single
- 他に、NSMakeRect(メソッド)、NSRect/NSSize(構造体)が必要ですが、それらはmacoslibからコピーさせて頂きました。(上記NSGlobalsにコピーする。)
おわりに
紙に印刷することはあまりないかもしれませんが、PDF出力が手軽にできるという点は使えるかも。
また、試しに用紙サイズを超える画像を貼り付けてみましたが、例1/例2とも、縦方向にはみ出した分は改ページされて印刷されましたが、横方向にはみ出した分は印刷されませんでした。テキストエディットは印刷範囲に収まるよう縮小していますので、同等の機能を付加する等した方がいいかもしれません。
なお、今回も仕組みを理解するために、極力シンプルな書き方を心懸けています。
(例えば、allocした変数の一部は然るべきタイミングでReleaseするべきと思われますが、そのためにはコードを工夫する必要があります。)
実用目的であれば、macoslibを利用させて頂くことを考えた方がいいでしょう。
お世話になったサイト
貴重な情報をご提供頂いている皆様に、お礼申し上げます。(以下、順不同)
参考サイト(1):About Printing on the Mac
参考サイト(2):NSApplication(モーダルダイアログの項)
参考サイト(3):Introduction to Sheets
更新履歴
2018.07.30 Xojoでの実装(例2)の22,27項を改訂
2016.10.01 Xojoでの実装(例2)の、18,19,20,23,24,25項で、NSTextViewPrintとすべきところがPrintSheetとなっていたので修正。
2016.09.24 新規作成
[Home] [MacSoft] [Donation] [History] [Privacy Policy] [Affiliate Policy]