株式会社クリアコード > ククログ

ククログ

タグ:

GNOME環境で作成したパスワード付きZIPが解凍できない時は

p7zipパッケージをシステムから取り除くと、 解凍できるZIPファイルが作れるようになるかもしれません。

こんにちは、クリアコードの藤本です。今回の記事では:

  1. GNOME標準のアーカイバでパスワード付きZIPファイルを作成したら、
  2. Windowsのエクスプローラやunzipコマンドで開けなかった。

という方向けに、この問題の原因と対策をお伝えしたいと思います。

問題の要点

GNOMEには標準のアーカイバとして「File Roller」というプログラムが付属しています。 普段意識して使うことは少ないかもしれないのですが、例えば、 Nautilusの「ファイルを圧縮する」メニューはこのプログラムが提供しており、 意外と多くの場面で利用されています。

今回取り上げる問題は、このプログラムでパスワード付きZIPファイルを作成すると、 他のプログラムで解凍できなくなることがある、というものです。 実際に、私の環境でFile Rollerを使ってパスワード付きZIPファイルを作成し、 unzipコマンドで解凍しようとすると、次のような警告が表示されます:

$ unzip  password.zip
Archive:  password.zip
   skipping: test.txt               need PK compat. v5.1 (can do v4.6)

また、このZIPファイルをWindows 10のエクスプローラで展開しようとすると、 例外が発生して処理が中断してしまいます。

予期しないエラーのため、ファイルをコピーできません。このエラーが再発する場合は、
エラーコードを利用して、この問題についてのヘルプを検索してください。

エラー 0x80004005: エラーを特定できません。
何が原因なのか?

この現象の本質的な原因は、File Rollerが「AES-128」でファイルを暗号化することにあります。

まず話の前提として、ZIPファイルの暗号化方式について簡単に整理します:

  1. もともと、ZIPには(PKZIP v1.0の時代から)単純なストリーム暗号が備え付けられており、 これが2018年現在も最もよく使われる暗号化方式となっています。

  2. しかし、この方式は暗号としては非常に弱く、 比較的簡単に突破できることが既に20年以上前から知られています。

  3. このため、「もっと強固な暗号化方式を導入して、セキュリティの向上を図ろう」という改良が、 いくつかのグループで進められており、その改良案の中でも現在最も有力なのが、 WinZipが2003年に導入したAESベースの暗号化仕様です。

  4. ただし「有力」とは言っても、他の実装の追従は極めて遅く、 公開から15年近く経った今でも、多くのZIPアーカイバはこの暗号化仕様をサポートしていません。

ここで、File Rollerに話を戻すと、 このプログラムは『7-Zipがインストールされている時は、AES-128を利用して暗号化する』 という実装になっています。システムに7-Zipが見つからない場合は、伝統的な暗号化方式が使われます。 File Rollerで生成したZIPファイルが、他のアーカイバで解凍できなくなったりする原因はここにあります。 要するに、実行時のシステムの状態によって、適用される暗号アルゴリズムがバックグラウンドで切り替わるのです。

実際に、先ほど解凍できなかったZIPアーカイブを検査してみると、 格納されているファイルがAES-128で暗号化されていることが確認できます:

$ 7z l -slt password.zip | grep Method
Method = AES-128 Store
暫定的な対応策

結論を先に述べると、File Rollerの暗号化方式を互換性のある旧方式に戻したい場合、 ユーザーには「7-Zipをアンインストールする」という選択肢しか現状はありません。 というのも、前節で解説した処理はソースに(暗号化アルゴリズムの選択含めて) ハードコードされており、設定やオプションで変えられる仕組みになっていないからです。

# Redhat系なら `dnf remove p7zip`
$ sudo apt remove p7zip-full

参考までに、以下に該当するソースコードの箇所を示します:

もちろん、適切な環境があれば、 該当箇所を変更した独自ビルドを作ることで問題を迂回することもできます。 しかし、手元でコードをいじって回避するよりは、 アップストリームで根本から問題を解決したいものです。

根本的な解決に向けて

そこで私たちは、今回の問題についてGNOMEプロジェクトに修正パッチを投げています。

https://gitlab.gnome.org/GNOME/file-roller/merge_requests/1

ただし、このパッチがそのまま取り込まれることはさほど期待しておらず、 これを起点に議論を進めていきたい、というのが基本的な立場です (というのも、最終的な解決までに議論が必要な論点がいくつかあるためです)。 詳細はパッチに付けた説明をご参照ください。

もし読者の皆様の中で、「これは」という意見をお持ちの方がいらっしゃれば、 ぜひとも上のスレッドにコメントをお寄せください。 皆様のご意見をお待ちしています。

2018-06-13

Firefox 62で修正されたFirefox 61での後退バグに見る、不要なコードを削除する事の大切さ

一般的に、プログラムの継続的な開発においては「機能の追加」や「不具合の修正」が行われる事は多いですが、「機能の削除」が明示的に行われる事はそう多くないのではないでしょうか*1。この度、使われなくなっていた機能が残っていた事により意図しない所で不具合が発生し、機能を削除することで不具合が解消された事例がありましたので、使われなくなった機能を削除する事の意義を示す一時例としてご紹介いたします。

発生していた現象

現在のFirefoxでは、インストールするアドオンは必ずMozillaによる電子署名が施されていなくてはならず、未署名のファイルからはアドオンをインストールできないようになっています。ただ、それではアドオンの開発そのものができないため、開発者向けの機能であるabout:debuggingから、開発中のアドオンを「一時的なアドオン」として読み込んで動作させられるようになっています。

この機能について、Firefox 61で「条件によっては、アドオンを一時的なアドオンとして読み込めない」「条件によっては、一時的なアドオンの読み込みに非常に時間がかかる」という不具合が発生していましたが、Firefox 62で修正される事になりました*2

現象が起こり始めたきっかけと、原因の調査

開発中のアドオンを一時的なアドオンとして読み込めないという現象について、現象発生のタイミングを二分探索で絞り込むツールであるmozregressionを使って調査した所、1452827 - XPIInstall has a bunch of cruft that needs to be cleaned upでの変更が投入されて以降発生するようになっていた事が分かりました。

また、この現象が発生していた場面では、WSL*3で作成したシンボリックリンクがリポジトリ内に含まれている場合に、開発者用のコンソールにwinLastError: 1920という情報が出力されるという状況でした。これはWindowsが返すファイル関連のエラーコードのひとつであるERROR_CANT_ACCESS_FILEを意味する物で、Bugzilla上において、上記変更の中に含まれているファイルの情報を取得する処理がWSLのシンボリックリンクに対してこのエラーを報告している、という情報を頂く事ができました。

実際の修正

ファイルの情報を調べようとして何らかのエラーが発生するファイルというものは、WSLのシンボリックリンク以外にも存在し得ます。また、一般的に、規模が大きな変更は予想外の影響をもたらすリスクがあり、変更は可能な限り最小限に留める事が望ましいとされます。これらの理由から、当初は「ファイルの情報を取得しようとした時のエラーを適切にハンドルすることで、特殊なファイルがあっても問題が起こらないようにする」という方向でパッチを作成し提出しました。

しかしながら、このパッチはレビューの結果却下され、そもそもこの「ファイルの情報を取得する処理」自体が不要だからそれを削除する方がよいのではないかという指摘を受けました。そこで改めて調査したところ、以下の状況である事を確認できました。

  • この処理は、アドオンの総ファイルサイズを計算する過程で呼ばれている。
  • しかしながら、現行のFirefoxではアドオンのファイルサイズは画面上に表示される事が無く、また、ファイルサイズの情報が有効に使われる部分も存在していない。
    • 過去には使われていた機能のようだが、機能の改廃が進んだ結果、現在は「ファイルサイズの計算が正しいかどうか」を検証する自動テストの中で参照されるだけの機能になっていた。
    • 類似の情報として、自動更新を通じてダウンロードされたアドオンのインストールパッケージのファイルサイズという物もある。
      • こちらは現在も使われているが、今回問題となっている「アドオンの総ファイルサイズ」とは別の物である。
  • よって、アドオンの総ファイルサイズという情報自体が現在では無用となっている。

既にある機能や情報の削除は、その機能を使っている箇所が1つでも残っていれば不可能なため、影響範囲の調査は特に念入りに行う必要があります。今回の事例では、

  • Mozillaのスタッフの人から、使われていない機能を削除する方向での対応が望ましい旨のコメントが出ていた。
  • 実際に調査した限りでは、確かに目に見える部分や他の機能からは参照されていないという事の確認が取れた。

という2つの根拠があったため、思い切って、アドオンの総ファイルサイズの計算に関わるコードと、サイズ情報を参照している箇所*4を削除するという内容でパッチを再作成しました。その結果、パッチは無事に取り込まれ、Firefox 62ではこの問題が修正される事になりました。

また、このパッチが取り込まれた結果、「条件によって一時的なアドオンの読み込みに非常に時間がかかる」という別のBugにも影響が及んでいた事が後になって分かりました。こちらのBugは、開発中のアドオンのフォルダー配下にnode_modulesのようなフォルダーがあると*5、その配下の大量のファイルをスキャンするために時間がかかるようになってしまった、という物です。アドオンの総ファイルサイズの計算自体を行わなくした事により、棚ぼた的にこちらのBugも解消されたという状況でした。

ただ、実はその後、「WSLのシンボリックリンクのせいでエラーが発生してアドオンを読み込めない」という状況自体が今となってはほぼ発生しなくなっているという事も分かりました。具体的には、Bugの報告時にエラーの原因になっていたシンボリックリンクはWindows 10 Creators UpdateやWindows 10 Fall Creators UpdateのWSLで作成された物でしたが、その後Windows 10 April 2018 UpdateのWSLなどではNTFSの妥当なシンボリックリンクが作成されるようWSLが改良されたため、この問題の影響を受けるのは「古いWSLで(npm installを実行するなどして間接的に作成される場合も含めて)シンボリックリンクを作って、それをその後もずっと使い続けている場合」だけという事になっています。一般ユーザーに影響が及ばないアドオン開発者向けの機能であるという事に加え、プラットフォーム側の改善により今では問題自体が発生しなくなっている(新たにシンボリックリンクを作り直すだけでよい)という事も相まって、このパッチはFirefox 61には取り込まれないという判断がなされています。

まとめ

以上、Firefox 62で取り込まれたパッチを題材に、既に不要となっていた機能が問題を引き起こしていた事例についてご紹介しました。

Firefox 56およびそれ以前のバージョンのFirefoxにおいて使用できていた従来型アドオンは、今回削除されたコードのような「Firefox内部では既に使われなくなった機能」を使用している事が度々ありました。そのためFirefox全体として、アドオンが動作しなくなる事を警戒して古いコードを大量に抱え込まざるを得ない状態が長く続いていました。Firefox57での「従来型アドオン(XULアドオン)廃止」という大きな変化には、このような状態を是正するためという意味合いもあったと言えるでしょう。これを反面教師として、不要になった機能や実装はその都度速やかに削除してコードを簡潔に保っていくという事の重要さを実感していただければ幸いです。

*1 ただし、機能一式の刷新にあたって「新しい実装に持ち越されなかった機能」が結果的に「旧バージョンから削除された機能」として見える、という事はあります。

*2 一般ユーザーへの影響は小さいと判断された結果、Firefox 61への修正の反映は見送られています。

*3 Windows Subsystem for Linux

*4 大半は自動テスト内で、UI上に現れる物もFirefoxでは使われていないThunderbird用に残されたコードに1箇所あるだけでした。

*5 静的な文法チェックを行うためにeslintを使っている場合などに、このような状況が発生し得ます。

2018-06-07

Gecko Embedded ESR60

はじめに

これまでにも何度か紹介してきましたが、クリアコードではGecko(Firefox)を組み込み機器向けに移植する取り組みを行っています。

当プロジェクトでは移植コストを少しでも低減するために、Firefoxの延長サポート版(ESR)のみを対象としています。これまではESR45やESR52をベースにハードウェアアクセラレーションに対応させるための作業を行ってきました。 現在はESR60に対応し、そのバグを修正する作業を進めています。

今回は、この作業の現在のステータスを紹介します。詳細なビルド方法については、Gecko Embedded 次期ESR対応 を参照してください。

現在のステータス

現在はFirefox ESR60の開発を進めています。ビルドは通るようになり、ソフトウェアレンダリングでの描画もある程度安定して動作するようになっています。Firefox 60からはWayland対応のコードも本体に入っているため、ソフトウェアレンダリングであればほぼ無修正で動作させることができています。 また、60ESRには入りませんでしたが、当プロジェクトが作成したEGLおよびOpenMAX対応のパッチも無事Firefox本体に取り込まれています。

ESR52で実現出来ていたハードウェア対応の移植作業については、2018年5月30日現在、以下のような状況です。

  • レイヤーアクセラレーション: 移植済み(RZ/G1M、R-Car M3、Raspberry Pi3 Model Bで検証)
  • OpenMAX(H.264デコード): 移植済み(RZ/G1Mで検証)
  • WebGL: 移植済み(R-Car M3で動作)
  • WebRTC: レイヤーアクセラレーションとの組み合わせでは動作せず(調査中)
  • Canvas 2D Contextのアクセラレーション: e10sオフでは動作

また、ESR52ではなかった要素として、Rustで書かれたコードが追加されてきている点が挙げられます。細かく動作検証はできていませんが、少なくともStylo(Quantum CSS)は実機(Renesas RZ/G1M)で動作することを確認できています。

前回からの改善点は以下の通りです。

  • EGL有効化時に表示されるUIと操作するときに反応する座標がずれる不具合が解消した
  • UIのタブのドラッグ&ドロップが動作するようになった
  • コピー&ペーストが動作するようになった
  • WebGLがR-Car M3で動作することを確認

現状では、以下の制限があります。

  • 動画を再生&一旦停止し、別の動画を再生した時にサウンドデバイスが解放されず、音が出力されない
  • SoCのGPUがOpenGL ES 2.0のプロファイルを持ちrobustness拡張をサポートしない場合、WebGLのコンテキストが不完全となるか、コンテキスト作成に失敗する
  • Yocto 2.4ベースのBSPではStyloのビルドが確認できていない
  • Renesas RZ/G1MのPowerVR SGX 544MPを使用する場合、コンポジターでハードウェア支援を有効にするとウィンドウリサイズ時に高確率でSEGVすることがある
  • e10s有効化時にEGLを有効化するとContentプロセスがSEGVする

動作確認を行ったハードウェア

現時点ではRenesas RZ/G1M、R-Car M3、また、コミュニティサポートのレベルですが、StyloのビルドサポートなしでRaspberry Pi3 Model Bにも移植しました。

まとめ

GeckoEmbeddedプロジェクトのESR60の状況とビルド方法について紹介しました。ESR60対応がひと段落し、対象ボードを拡充する対応は少しづつ進んでいるものの、まだまだ手が足りていない状況です。興味を持たれた方は、ぜひ当プロジェクトに参加して頂ければ幸いです。

つづき: 2018-06-15
2018-05-30

Gecko Embeddedプロジェクト 11月時点のステータス

はじめに

2017年7月6日の記事で紹介した通り、クリアコードは組み込みLinux向けにMozilla Firefoxを移植するプロジェクトGecko EmbeddedWebDINO Japan(旧Mozilla Japan)様と共同で立ち上げ、開発を進めております。Yoctoを使用してFirefoxをビルドしたりハードウェアクセラレーションを有効化する際のノウハウを蓄積して公開することで、同じ問題に悩む開発者の助けになることを目指しています。

その後も継続的に改善を進めており、いくつか進展がありましたので、11月時点のステータスを紹介します。

現在のステータス

ターゲットボードの追加

7月時点では、Firefox ESR52のビルド及び動作はRenesas RZ/G1E Starter Kitのみで確認していましたが、その後iWave RainboW-G20D Q7および Renesas R-Car Gen3でも同様に確認しています。ビルド方法は以下のページにまとめてあります。

Wayland対応のバグ修正

Firefoxは正式にはWaylandをサポートしていませんが、Red HatのMartin Stransky氏がWaylandへの移植作業を行っています。 Stransky氏のパッチはFirefoxの最新バージョンを対象としていますが、Gecko Embeddedプロジェクトではこのパッチを52ESRのツリーに移植した上で、安定化作業を進めています。 本プロジェクトで発見した問題や、作成したパッチはStransky氏に随時フィードバックしています。

最近では以下のような報告を行っています。

WebRTC対応

Wayland版FirefoxではWebRTCでビデオチャットを行おうとするとクラッシュする問題が存在していましたが、この問題を修正し、WebRTCが利用できるようになりました。

WebGL対応

7月時点ではドライバに絡む問題を解決できていなかったためビルド時点でのWebGL有効化手段を提供していませんでしたが、その後問題を解決できたため、パッチを更新しYoctoレシピにビルドオプションを追加しています。

WebGLを有効化するには、ビルド時に以下の設定をYoctoのlocal.confに追加して下さい。

PACKAGECONFIG_append_pn-firefox = " webgl "

また、今回新たに対象ボードとして加えたR-Car Gen3は非常に強力なため、フルHDの解像度でもWebGLが快適に動作することを確認しています。

Canvas 2D Contextのアクセラレーション

WebGLと同様に、Canvas 2D Contextのアクセラレーションについてもドライバに絡む問題を解決できたため、Yoctoレシピにビルドオプションを追加しています。

アクセラレーションを有効化するには、ビルド時に以下の設定をYoctoのlocal.confに追加して下さい。

PACKAGECONFIG_append_pn-firefox = " canvas-gpu "

まとめ

Gecko Embeddedプロジェクトの2017年11月現在のステータスについて紹介しました。 安定化や、さらなるパフォーマンス改善、他のSoCのサポートなど、やるべきことはまだまだ残されていますので、 興味がある方は協力して頂けるとありがたいです。問題を発見した場合はGecko EmbeddedプロジェクトのIssueページに報告して下さい。

2017-11-09

GeckoエンジンにmacOSでprintToFileの機能を実装してみた話

はじめに

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印刷を行うには | ククログ にあるように、NSPrintSaveJobNSPrintJobSavingURL のような値が設定されているかを探します。

ここで、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();

のコードにより取り扱うことができます。

ここではGetCocoaUnitkPaperSizeMillimeterskPaperSizeInches を返すprotectedメソッドです。また、SetCocoaPaperSizeは印刷用紙の大きさを設定するメソッドです。

まとめ

macOSでもXULアプリケーションなどから printToFile = true を設定したときにPDFへ出力するパッチについて解説しました。 このBugのレポート日時は 2011-08-01 12:35 PDT なので、このパッチで5年半越しの問題が解決されました。 mozilla-centralの該当コミットを見るとBug番号がまだ6桁であり、周辺のコミットに紐づいたBug番号は7桁なので感慨深いですね。

つづき: 2017-03-10
2017-02-22

2008|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|
タグ: