第17回 androidアプリでPDFを表示する

先月中旬にMicrosoftがXamarinを買収し、
先月末にXamarin Studioなどが無料化されました。
これによりCommunity Editionを含むVisual Studio 2015にXamarinの関連プラグインが同梱され、
Visual Studio上のC#でandroidやiOSデバイス用アプリの開発が無料で可能になっています。

今回はVisual Studio 2015を利用してandroidで縦書きPDFを横書き表示するアプリを作成してみます。

処理自体は前回までの作業でほぼできているため、
実装が必要なのはandroid固有のUI操作とテキスト表示処理のみです。



まずはandroidプロジェクトの準備。

(Xamarinがインストールされている)Visual Studio 2015を起動し、
「ファイル」メニューにある「新規作成」から「プロジェクト」を選択。
「テンプレート」は「Visual C#」にある「andoroid」の「Blank App (Android)」を選択。
プロジェクト名は「PDFAndroid」としました。

プロジェクトが自動生成されたら、
「プロジェクト」メニューから「PDFAndroidのプロパティ」を選択し、プロジェクト設定画面を開きます。
「Application」タブにある「Compile using Android version」を手持ちのandroidデバイスのOSバージョンに合わせて設定します。私は「Android 4.3 (Jelly Bean)」にしました。
(この設定を間違えて新しいOSバージョンのままだと配置などができません)

androidスマートフォンをUSBデバッグ有効にしてPCへ接続すると、
ツールバーのデバッグ開始ボタンの横にスマートフォンの名前とOSバージョンが表示されます。
ここで試しに実行するとビルドされ、アプリがスマートフォンへ転送(配置)され、何も機能のないアプリが起動します。



androidプロジェクトの準備ができたら一気に実装します。
と言っても作業は前回までのソースコードをandroidプロジェクト内にコピペするのがほとんどです。
この辺は本当にC#様さまです。UIに関連する部分以外でプラットフォーム間の差異を感じることは(たまにしか)ありません。

・PDFPage.cs
・PDFRawReader.cs
・PDFTextReader.cs
の3ファイルを前回のプロジェクトからandroidプロジェクトのフォルダへコピー。
そして「プロジェクト」メニューの「既存の項目の追加」からそのファイルをプロジェクトへ追加します。

最後にMainActivity.csに表示/操作処理を用意すれば終わりです。

これで縦書きPDFを横書き表示できました。

ただし・・・ものすごく遅いです。最初にPDFを全部読むために数十秒かかります。
読み終わった後のページ送りなどは軽いのですが実用には向かない速度でした。
実用性を考えるなら根本的に処理を見なおしたほうがよさそうです。

■MainActivity.cs
using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using Android.Graphics;
using System.IO;
using Pdf2Pdf;

namespace PDFAndroid
{
	class MyView : View
	{
		public MyView(Context context)
			: base(context)
		{
			SetBackgroundColor(Color.Gray);
		}

		int _nPage = 0;
		PDFTextReader _pr = null;


		public override void Draw(Canvas canvas)
		{
			base.Draw(canvas);

			//「小説家になろう」からダウンロードした縦書きPDFを指定
			//パスはandroid内のPDF保存場所を指定
			string strPDFFile = "/storage/emulated/0/N9442CW.pdf";

			if (File.Exists(strPDFFile))
			{
				if (_pr == null)
				{
					_pr = new PDFTextReader();
					_pr.Read(strPDFFile);
				}

				DrawPage(_pr, _nPage, canvas);
			}
			else
			{
				using (Paint paint = new Paint())
				{
					paint.Color = Color.Black;
					canvas.DrawText("PDFが見つかりません", 50, 50, paint);
				}
			}
		}

		public void PageNext()
		{
			if (_pr == null || _pr.Pages.Count == 0)
				return;

			_nPage++;
			if (_nPage >= _pr.Pages.Count)
				_nPage = _pr.Pages.Count - 1;

			Invalidate();
		}

		public void PageBack()
		{
			if (_pr == null || _pr.Pages.Count == 0)
				return;

			_nPage--;
			if (_nPage < 0)
				_nPage = 0;

			Invalidate();
		}


		//横書きに変換表示
		bool DrawPage(PDFTextReader pr, int nPage, Canvas canvas)
		{
			if (nPage < 0 || nPage >= pr.Pages.Count)
				return false;

			PDFPage page = pr.Pages[nPage];

			int nWidth = Width;
			int nHeight = Height;

			using (Paint paint = new Paint())
			{
				paint.Color = Color.Black;

				float y = 10;
				foreach (object obj in page.Items)
				{
					if (obj.GetType() != typeof(PDFText))
						continue;

					PDFText text = (obj as PDFText);

					//(0,0)はページ左下
					//A4縦は(595,842)がページ右上
					//A4横は(842,595)がページ右上

					//ページ番号は描画しない
					//ページ番号のy座標は56.7?
					if ((int)(text.fY) == 56)
						continue;

					float x = 10;

					//表紙(i == 0)と最終ページ以外はオリジナルの行間隔で表示
					if (nPage > 0 && nPage < _pr.Pages.Count - 1)
						y = nHeight - (int)(text.fX * nHeight / 842.0);
					else
						y += 30;        //表紙(i == 0)と最終ページは固定行間隔で表示

					paint.TextSize = text.fPoint * 1.5f;

					canvas.DrawText(text.strText, x, y, paint);
				}
			}

			return true;
		}
	}




	[Activity(Label = "PDFAndroid", MainLauncher = true, Icon = "@drawable/icon")]
	public class MainActivity : Activity
	{
		MyView _view = null;

		protected override void OnCreate(Bundle bundle)
		{
			base.OnCreate(bundle);

			RequestWindowFeature(WindowFeatures.NoTitle);
			SetContentView(Resource.Layout.Main);

			LinearLayout layout = new LinearLayout(this);
			SetContentView(layout);

			_view = new MyView(this);
			layout.AddView(_view);
		}



		//前回タッチした座標を記録
		float _fLastX = 0;
		float _fLastY = 0;
		bool _bMove = false;

		/// <summary>
		/// タッチイベント処理
		/// </summary>
		public override bool OnTouchEvent(MotionEvent e)
		{
			if (e.Action == MotionEventActions.Down)
			{
				_fLastX = e.RawX;
				_fLastY = e.RawY;
				_bMove = false;
			}

			if (e.Action == MotionEventActions.Move)
			{
				_bMove = true;      //スライドした
			}

			if (e.Action == MotionEventActions.Up)
			{
				if (_bMove && Math.Abs(_fLastX - e.RawX) > 50)      //ちょっとしかスライドしなかったときは無反応
				{
					if (_fLastX < e.RawX)
					{
						//左から右にスライドされた
						_view.PageBack();
					}
					else
					{
						//右から左にスライドされた
						_view.PageNext();
					}
				}

				_bMove = false;
			}

			return base.OnTouchEvent(e);
		}

	}
}

プロジェクトファイルをダウンロード


カテゴリー「PDFを処理する(C#)」 のエントリー