はじめに
Geckoエンジンはクロスプラットフォームを標榜して作成されています。また、できるだけそのプラットフォームの特長を生かすため、そのプラットフォーム特有のAPIを使用するように作られている箇所もあります。印刷に関連するGeckoのコードももちろんプラットフォームごとに異なるAPIを呼ぶようになっており、通常使用する限りにおいてはどのプラットフォームも一様に印刷の機能を使用することができます。
今回の記事は nsIWebBrowserPrint
に紐づいているprintという印刷のためのAPIにmacOSではPDFを印刷するのに必要な情報が渡っていなかった問題を解決した、という題材について書きます。
Geckoでの印刷の流れ
Geckoでは印刷をするときにプラットフォーム固有のAPIを使用する箇所とプラットフォーム非依存のAPIの箇所があります。
プラットフォームに依存するモジュールはサービス化されており、実行時に適切なContract ID(CID)を持ったモジュールがロードされる仕組みとなっています。このCIDはJavaScript側からも見え、アドオンやXULアプリケーション上でもこのCIDを用いて適宜必要なモジュールをロードしていきます。
印刷の流れを追うと以下のようになります。
nsDocumentViewer::Print(...)
-> nsPrintEngine::Print(...)
-> nsPrintEngine::DoCommonPrint(...)
-> printDeviceContext->InitForPrinting(aDevice, ...) // プラットフォームごとに別々のDeviceContextが作成されるのでそれを用いる
-> aDevice->MakePrintTarget(..)
-> nsPrintEngine::DoCommonPrint(...) //戻ってきたら印刷プレビューか印刷ジョブを行う
macOSのCocoaのAPIでの実装はwidget/cocoa以下に配置されています。
macOSでprintToFileが動かない問題のBug
macOSではnsIPrintSettingsServiceの関数のprintToFileへtrueを渡してもPDFヘ出力されないというBugが長年に渡って未解決です。詳細はMozillaのBugzillaのprintToFile is busted on Mac | Mozilla Bugzillaを参照してください。
macOSでGeckoのレンダリング結果をPDFへ出力できなかったのはなぜか
macOSでPDFへの出力をさせるのに必要な情報が上記Bugのパッチがコミットされる前のコードで設定されているかどうかを見ます。
macOSでCocoaのAPIからPDF印刷を行うには | ククログ にあるように、NSPrintSaveJob
や NSPrintJobSavingURL
のような値が設定されているかを探します。
ここで、macOSでPDF印刷ができないのはnsIPrintSettingsServiceのCIDを持つ https://hg.mozilla.org/mozilla-central/file/0ca553b86af3/widget/cocoa/nsPrintSettingsX.mm にこれらの値がないかどうかを調べれば原因が分かります。
探すと見事にそのようなことをしている箇所はありませんでした。 前の記事を元にGeckoに向けたパッチを書く必要があります。
GeckoのXPCOMで生成されたクラスのAPIの振る舞いをプラットフォーム固有にする
GeckoはXPCOMの技術を用いており、インターフェースはidlファイルで定義されています。 ここではnsIPrintSettingsのidlは https://dxr.mozilla.org/mozilla-central/source/widget/nsIPrintSettings.idl です。
GeckoはC++で書かれているため、nsPrintSettings
クラスに定義されているメソッドで必要なものをoverrideしてやれば目的は達成できます。
そのため、SetToFileName(const char16_t *aToFileName)
をoverrideします。
diff --git a/widget/cocoa/nsPrintSettingsX.h b/widget/cocoa/nsPrintSettingsX.h
--- a/widget/cocoa/nsPrintSettingsX.h
+++ b/widget/cocoa/nsPrintSettingsX.h
@@ -51,8 +51,10 @@ public:
void SetInchesScale(float aWidthScale, float aHeightScale);
void GetInchesScale(float *aWidthScale, float *aHeightScale);
+ NS_IMETHOD SetToFileName(const char16_t *aToFileName) override;
+
void SetAdjustedPaperSize(double aWidth, double aHeight);
void GetAdjustedPaperSize(double *aWidth, double *aHeight);
diff --git a/widget/cocoa/nsPrintSettingsX.mm b/widget/cocoa/nsPrintSettingsX.mm
--- a/widget/cocoa/nsPrintSettingsX.mm
+++ b/widget/cocoa/nsPrintSettingsX.mm
@@ -270,3 +275,30 @@ void nsPrintSettingsX::GetAdjustedPaperS
*aWidth = mAdjustedPaperWidth;
*aHeight = mAdjustedPaperHeight;
}
+
+NS_IMETHODIMP
+nsPrintSettingsX::SetToFileName(const char16_t *aToFileName)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NSMutableDictionary* printInfoDict = [mPrintInfo dictionary];
+ nsString filename = nsDependentString(aToFileName);
+
+ NSURL* jobSavingURL =
+ [NSURL fileURLWithPath: nsCocoaUtils::ToNSString(filename)];
+ if (jobSavingURL) {
+ [printInfoDict setObject: NSPrintSaveJob forKey: NSPrintJobDisposition];
+ [printInfoDict setObject: jobSavingURL forKey: NSPrintJobSavingURL];
+ }
+ NSPrintInfo* newPrintInfo =
+ [[NSPrintInfo alloc] initWithDictionary: printInfoDict];
+ if (NS_WARN_IF(!newPrintInfo)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ SetCocoaPrintInfo(newPrintInfo);
+ [newPrintInfo release];
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
ここまででPDFに出力するための関数の実装です。
まだmacOS向けにはoverrideする必要がある関数が残っています:
-
用紙の単位
- NS_IMETHOD SetPaperSizeUnit(int16_t aPaperSizeUnit) override;
-
拡大縮小倍率
- NS_IMETHOD SetScaling(double aScaling) override;
-
縦横
-
NS_IMETHOD GetOrientation(int32_t *aOrientation) override;
-
NS_IMETHOD SetOrientation(int32_t aOrientation) override;
-
-
余白(GeckoではMerginに加えてUnwritaebleMerginという設定値が存在する)
-
NS_IMETHOD SetUnwriteableMarginTop(double aUnwriteableMarginTop) override;
-
NS_IMETHOD SetUnwriteableMarginLeft(double aUnwriteableMarginLeft) override;
-
NS_IMETHOD SetUnwriteableMarginBottom(double aUnwriteableMarginBottom) override;
-
NS_IMETHOD SetUnwriteableMarginRight(double aUnwriteableMarginRight) override;
-
これらと、印刷用紙の単位変換を行なう処理を追加したものがこちらです。
このmozilla-centralのパッチではGeckoが印刷に用いている二つの単位系の対応も入っています。
-
インチ
- kPaperSizeInches
-
ミリメーター
- kPaperSizeMillimeters
の両方の単位系がGeckoでの印刷ではサポートされています。
そのため、
-
Inches -> Twips -> Pixels
-
Millimeters -> Twips -> Pixels
の両方を取り扱う必要があります。これは
diff --git a/widget/cocoa/nsPrintSettingsX.mm b/widget/cocoa/nsPrintSettingsX.mm
--- a/widget/cocoa/nsPrintSettingsX.mm
+++ b/widget/cocoa/nsPrintSettingsX.mm
@@ -254,8 +254,13 @@ NS_IMETHODIMP nsPrintSettingsX::SetPaper
NS_IMETHODIMP
nsPrintSettingsX::GetEffectivePageSize(double *aWidth, double *aHeight)
{
- *aWidth = NS_INCHES_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
- *aHeight = NS_INCHES_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
+ if (kPaperSizeInches == GetCocoaUnit(mPaperSizeUnit)) {
+ *aWidth = NS_INCHES_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
+ *aHeight = NS_INCHES_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
+ } else {
+ *aWidth = NS_MILLIMETERS_TO_TWIPS(mAdjustedPaperWidth / mWidthScale);
+ *aHeight = NS_MILLIMETERS_TO_TWIPS(mAdjustedPaperHeight / mHeightScale);
+ }
return NS_OK;
}
diff --git a/widget/cocoa/nsDeviceContextSpecX.mm b/widget/cocoa/nsDeviceContextSpecX.mm
--- a/widget/cocoa/nsDeviceContextSpecX.mm
+++ b/widget/cocoa/nsDeviceContextSpecX.mm
@@ -49,6 +49,19 @@ NS_IMETHODIMP nsDeviceContextSpecX::Init
if (!settings)
return NS_ERROR_NO_INTERFACE;
+ bool toFile;
+ settings->GetPrintToFile(&toFile);
+
+ bool toPrinter = !toFile && !aIsPrintPreview;
+ if (!toPrinter) {
+ double width, height;
+ settings->GetEffectivePageSize(&width, &height);
+ width /= TWIPS_PER_POINT_FLOAT;
+ height /= TWIPS_PER_POINT_FLOAT;
+
+ settings->SetCocoaPaperSize(width, height);
+ }
+
mPrintSession = settings->GetPMPrintSession();
::PMRetain(mPrintSession);
mPageFormat = settings->GetPMPageFormat();
のコードにより取り扱うことができます。
ここではGetCocoaUnit
は kPaperSizeMillimeters
か kPaperSizeInches
を返すprotectedメソッドです。また、SetCocoaPaperSize
は印刷用紙の大きさを設定するメソッドです。
まとめ
macOSでもXULアプリケーションなどから printToFile = true
を設定したときにPDFへ出力するパッチについて解説しました。
このBugのレポート日時は 2011-08-01 12:35 PDT
なので、このパッチで5年半越しの問題が解決されました。
mozilla-centralの該当コミットを見るとBug番号がまだ6桁であり、周辺のコミットに紐づいたBug番号は7桁なので感慨深いですね。