全力わはー RSSフィード

2016-06-27

TPdfDocument クラス。

| 01:11 | TPdfDocument クラス。を含むブックマーク

f:id:tales:20160627010035p:image:w360

これは何?

前回の成果を簡単なクラスにまとめて、Delphi的にCreateしてLoadFromFileで読み込み的なやつです。読み込んでExportAsImageで画像化するシンプルなクラスになってます。

使い方

デフォルト設定で使う場合はめちゃくちゃシンプルです。

uses
  ..., PdfDoc; // <-今回作成したユニット
var
  pdf: TPdfDocument;
begin
  pdf := TPdfDocument.Create;
  try
    pdf.LoadFromFile('C:\Sample\Sample.pdf',
      procedure
      begin
        pdf[0].ExportAsImage('C:\Sample\Sample_page1.png');
      end);
  fianlly
    pdf.Free;
  end;
end;

以上のコードでPDFを読み込んで1ページ目をPNGファイルとして保存します。

LoadFromFileで無名メソッドを指定してますが、WinRTのファイル読み込みは非同期なのでLoadFromFile直後ではまだロードが終わっていない可能性があり、読み込み完了通知用のコールバックを指定する必要があります。また、ExportAsImageでのファイル保存も非同期なので、上記コードでは省略していますが同じようにコールバックを書いて保存終了通知を受け取ることができます。

ExportAsImageメソッドは省略されたオプションオーバーロードがいくつかあり、正式には以下のようになっています。

procedure ExportAsImage(Adapter: IStream; FileType: TExportFileType = eftPng;
  DestWidth: UInt32 = 0; DestHeight: UInt32 = 0; CompleteProc: TProc = nil); overload;
procedure ExportAsImage(Stream: TStream; FileType: TExportFileType = eftPng;
  DestWidth: UInt32 = 0; DestHeight: UInt32 = 0; CompleteProc: TProc = nil); overload;
procedure ExportAsImage(const FileName: string; FileType: TExportFileType = eftPng;
  DestWidth: UInt32 = 0; DestHeight: UInt32 = 0; CompleteProc: TProc = nil); overload;

1つ目の引数は保存先の指定です。3つのオーバーロード中、StreamやFileNameは特に解説せずともよくあるストリームとファイルへの書き出しなので分かると思いますが、IStreamへの書き出しは単にStreamとFileNameのメソッドで内部的に使用するものです。privateにしても良かったんですが、IStreamを直接指定できた方が楽な場合もあるので一応。

2つ目のFileTypeですが、指定するとエクスポートする画像の形式を指定できます。デフォルトではPNG形式ですが、BMPJPEGなど他の形式も指定可能です。

3つ目と4つ目のDestWidthとDestHeightは出力画像サイズになります。0を指定するとページの縦横サイズがそのまま使用されます。両方指定するとそのサイズに拡大縮小され、どちらか一方だけ指定するともう片方のサイズはアスペクト比を考慮して自動的に計算されます。例えば横:縦=3:2のPDFの場合、DestWidthに600を指定してDestHeightはデフォルトのまま(=0)にすると、DestHeightは自動的に400が指定されます。

CompleteProcは完了通知を受け取ります。TPdfDocument.OnExportCompleteイベントでも受け取れますが、両方指定した場合はこのCompleteProcが優先されます(LoadFromFileのCompleteProcとOnLoadCompleteイベントも同じ仕様です)。

なお、上記のコードはパスさえ通せばユニットをインストールせずとも使えますが、TPdfDocumentはTComponentを継承する非ビジュアルコンポーネントにもなっているので、インストールしてフォーム上にTPdfDocumentを貼り付けることで、IDE上でOnLoadCompleteやOnExportCompleteイベントをセットするような使い方も可能です。

ダウンロード

ユニットとサンプルをダウンロード

この中に入っているPdfDoc.pasが今回追加したユニットです。他のラッパーユニットと同じようにパスの通ったところに置くか、そのフォルダ自体をパスに追加して使ってください。サンプルの方はProject1.dprがDirect2Dを使った前回のサンプルで、Project2.dprがPdfDoc.pasを使った簡易PDFビューア的なサンプルになります。動作環境はOS側はWindows 10Delphi側はWinRTが入手できるバージョン(後期のXEシリーズや10.x系)であれば動くと思います。サンプルコード内ではVCLを使ってますが、PdfDocユニット自体は特にVCL依存は無いので、FMXでも吐き出したTStreamを(FMXの)TBitmapクラスに食べさせれば画像を取得できるはず。

あとライセンスを書くのを忘れてましたが、Boost Software License 1.0です。

2016-06-25

外部ライブラリ無しでPDFを描画する。

| 00:45 | 外部ライブラリ無しでPDFを描画する。を含むブックマーク

WinRTって知ってる?

f:id:tales:20160625001654p:image:w360

WinRTとは、Windowsストアアプリ専用のAPIセット…ではないです。すでにトーストAPIなどを使っていてご存じの方もいると思いますが、デスクトップからも利用可能なCOMベースのAPIです。

Windows 8の時点だとWinRTは未来があるのか怪しいAPIでしたが、Windows 10Windowsの最終バージョンであるとMS宣言し、そのWindows 10に載っているAPIなのでWin32APIと同等に扱っても良さそうです。

参考

特集:デスクトップでもWinRT活用:開発者が知っておくべき、ライブラリとしてのWindowsランタイム (1/5) - @IT

WinRTでできること

ストアアプリは基本的にこのAPIを使って構築することになるので、上述のトーストAPIで画面に通知を出すこと以外にもとにかく色んなことができます。

で、その中にはPDFを扱うAPIもあります。こちらのブログ経由で知ったんですが、これを利用するとどうやら外部ライブラリを使わずにPDFを扱えるようです。

WinRTは腰の重いエンバカデロには珍しいことに、すでにラッパーユニットが用意されています。10 Seattle以降では標準搭載、それ以前ではGetIt経由で入手可能です。

しかし…

じゃあこれでPDF APIも使用可能かというと、残念ながらPDF関連のヘッダはほぼ全く移植されてませんでした(関連する列挙型が1つあるだけ)。

無いなら自分でやればいいということで、さくっと移植。これでようやくAPIが使える…かというとそうは問屋が卸さず。

PDF描画の心臓部であるIPdfRendererNativeインターフェースのRenderPageToDeviceContextメソッド引数として使われているID2D1DeviceContextが存在しないとコンパイラ様が仰るわけです。

怠慢ですよねぇ

DelphiにはWinapi.D2D1ユニットがはじめから入ってます。Direct2D用のユニットです。これはd2d1.h(とそれに必要な他のいくつかのヘッダ)を移植したもので、これ自体には問題ありません*1。実はDirect2Dにはバージョンがあり、Vista/7で導入された1.0、8で導入された1.1、8.1で導入された1.2、10で導入された1.3があります。d2d1.hはこのうち1.0のみを扱っており、1.1はd2d1_1.hが、1.2はd2d1_2.hが、1.3はd2d1_3.hが必要になります。なりますが、Delphiにはそれらを移植したユニットは存在しません。

大事なことなのでもう一度言いますが、Delphiにはそれらを移植したユニットは存在しません。新バージョンが発売されて「WINDOWS 10の最新機能をサポート」なんてエンバカデロは言ってますが、やはりDelphiにはそれらを移植したユニットは存在しません。エンバカデロの「Windows XXに対応!」はセールス的に見栄えのするUI系やセンサーのサポートみたいなのはやってくれるんですけど、そのOSで追加された機能の大半、特に裏方的な機能はほんとに対応してくれません。PDFはまだいいとしても、Direct2DはGDI/GDI+の後継APIでかなり重要なので、こんな状態でサポートとか言うのほんと詐欺なんでやめて欲しい…。

もっと頑張って移植

なんて愚痴を言ってるときりがないので、諦めて自分で移植します。幸いなことにID2D1DeviceContextはDirect2D 1.1で導入されたので、d2d1_1.hだけを移植すれば良さそうです。「だけ」と言いつつ、d2d1_1.hは5000行を超すサイズなのでPDFヘッダを移植する時の手軽さでは全然ないんですが、これも何とか移植しました。

ようやく本編

これでPDF関連のユニット(WinAPI.Data.Pdf.pas, WinAPI.Data.Pdf.Interop.pas)とそれに必要なDirect2D用ユニット(Winapi.D2D1_1.pas)が揃いました。あとは前述のブログを参考に実装するだけです。本質部分とは関係ないDirect2Dの初期化だけで結構量があり、ここに載せるにはちょっと長いので必須ユニットとサンプルをまとめてアップしますが、こんな感じでフォームにPDFを表示できます。

f:id:tales:20160625002307p:image:w360

ちなみに表示だけじゃなく右クリックで画像として保存可能ですが、フォーム上に表示するのと画像化では処理が異なり、前者はIPdfRendererNativeとIPdfDocumentインターフェースを、後者はIPdfDocumentインターフェースのみを使っています。前者はID2D1DeviceContextやIDXGISurfaceのように画面上に書き出すためのもので、後者はIRandomAccessStreamなどストリームに書き出すことができます。特に後者はDirect2D等を必要としないので、PdfDocumentのインスタンスさえ作れば簡単に扱うことが可能です。IStreamにも書き出せるので、IStreamを実装してTMemoryStream辺りに書き出すようにして、それをDelphi側の画像クラスに食わせて画面表示した方が全体としては楽そうです。

ダウンロード

ユニットとサンプルをダウンロード

サンプルを試してみる時は、libフォルダをパスに追加するか、中身をパスの通った場所に置いてください。

動作環境ですが、MSDNを見るとWindows 8.1でも使えるのかWindows 10じゃないと使えないのかAPIやページによってまちまちでよく分からないんですが、少なくとも手元のWindows 10+10.1 Berlin上では動いてます。

余談

Direct2Dの初期化も、Direct2D 1.0のID2D1RenderTargetを使ったものはVcl.Direct2D内に実装されているんですが、今はID2D1DeviceContextを使ったものが主流で、例えばWIC(Windows Imaging Component)なんかでID2D1DeviceContextを必要とするメソッドがあるんですよね。起点となるインターフェースなので本当に重要なんです。

まあそのWICのメソッドってのもWindows 8で追加された新しいインターフェースで追加されたもの(かつ未移植)なので、そもそも使えないんですけどね…。

*1:まぁSDKバージョンアップで更新された部分は全然反映されてないんだけど

2016-06-03

Oculus SDK 1.4.0 wrapper for Delphi

| 23:27 | Oculus SDK 1.4.0 wrapper for Delphiを含むブックマーク

ダウンロード

今頃CV1が届いてるはず…そんなふうに考えていた時期が俺にもありました

出荷予定日:5/16/2016 - 5/26/2016

なのに未だ出荷されず。Oculusェ…。

2016-05-06

Oculus SDK 1.3.2 wrapper for Delphi (と、いくつかデモ)

| 00:17 | Oculus SDK 1.3.2 wrapper for Delphi (と、いくつかデモ)を含むブックマーク

f:id:tales:20160506000858g:image

久々に

0.0.1のリリースからかなり間が空きましたが、もうすぐCV1が届くはずなのでリハビリも兼ねてヘッダを移植してみました。

今回はそれに加えて、元々入っているOculusRoomTiny系のデモもいくつか移植しています。コンパイル済みのEXE(64bit用)も入れてあるので、一応Riftさえあればすぐに試せます。

ダウンロード

ちなみに

デモには簡易3Dフレームワークみたいなのが付いてるので、3Dモデルの追加表示程度なら割と簡単にできます*1

f:id:tales:20160506002643g:image

*1:動かすのは別として

2016-04-22