・目的
日本語PDFをandroidで出力しよーとすると、haruしかまともなものがないっぽい。
でも、haruでndkつかってビルドとかメンドイので、JSで手軽に出力する。
swiftとkotlinは似てるのし、pdf処理をjsで書いておけば共通化できるので、
楽できるかという目論見。。。
・前準備
[備忘録]アプリ上でpdfMakeにて日本語PDF生成(swift編)と同じ。
ようは使う日本語TrueTypeFontのJS化
・実装
※androidstudio使う。kotlin編なので、kotlinは導入済みとする。
androidstudioでemptyactivityのプロジェクトを作成。
バージョンは、古い方のnexus7しかもってないので、api21のロリポップとした。
プロジェクトをkotlinにする。MainActivity(デフォルトでつくったからそのまま)も、kotlin化
初期状態ではassetsフォルダを作る
場所は、
/プロジェクトフォルダ/app/src/main/
こんな感じ。
PDFを生成するJSが乗ってるhtmlを作成
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
</head>
<body>
<button id="btn_gen_pdf" type="button">日本語PDF出力</button><br/><br/>
<script src="js/jquery.min.js"></script>
<script src="js/pdfmake.js"></script>
<script src="js/vfs_fonts.js"></script>
<script>
var docDefinition;
// 初期化処理
$(function() {
// PDF出力定義
pdfMake.fonts = {
tekito_font: {
normal: 'ipaexm.ttf'
}
};
docDefinition = {
pageSize: 'A4',
content: [
{text: 'This is an sample PDF printed with pdfMake. 日本語のテスト',
fontSize: 10,
absolutePosition : { x :15,y :15 }
}],
defaultStyle: {font: 'tekito_font'}
};
$("#btn_gen_pdf").on("click", function(){
var _hoge = pdfMake.createPdf(docDefinition);
_hoge.getDataUrl(function(result) {
// PDF生成(Base64エンコードされている)
var encPdf = result.replace(/^data:application\/pdf;base64,/, '');
// "NativeObj"として、WebViewに紐付けられている場合
if (NativeObj) {
// エンコードされた文字列をアプリ上でデコードし、ファイルを出力
var genPdfPath = NativeObj.generatePdfOnNative(encPdf);
console.log("generated:"+genPdfPath);
} else {
// とりあえず。。。
console.log("========");
console.log("local log:\n" + encPdf);
}
});
});
});
</script>
</body>
</html>
iosとの違いは、iosの場合、JSでPDF生成後、フォームをPostして
そのPostをWebViewのデリゲートでキャプチャして処理するが、
AndroidのWebViewの場合は、WebViewのインスタンスにJava(Kotlin)のオブジェクトを
差し込み、JSでPDF生成後(エンコードされた文字列)、JavaオブジェクトをJSの
グローバルオブジェクト?扱いで参照して、そのメソッドで処理する。
また、iosのwebviewは直接PDFを開く事が出来るが、androidのwebviewは
直接開けないないので、android側のImageViewで描画する事にした。
画面レイアウトに、WebView、ImageView追加
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="gkgkdrink.com.jspdftest.MainActivity">
<WebView
android:id="@+id/wb_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
</WebView>
<ImageView
android:id="@+id/pdf_preview"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
ImageViewは、直接WebViewに生成したPDFを表示できないので、追加した。。。
なんとかならんのかな。。。。
MainActivityのソース
package gkgkdrink.com.jspdftest
import java.io.File
import java.io.FileOutputStream
import java.io.BufferedOutputStream
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.util.Log
import android.webkit.WebView
import android.webkit.JavascriptInterface
import android.widget.ImageView
import android.util.Base64
import android.os.ParcelFileDescriptor
import android.graphics.Bitmap
import android.graphics.Rect
import android.graphics.pdf.PdfRenderer
import android.os.Looper
import android.os.Handler
class MainActivity : AppCompatActivity() {
var wb: WebView? = null
var storeDir: String? = null
var pdfBitMap: Bitmap? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
this.wb = this.findViewById(R.id.wb_main) as WebView
storeDir = this.applicationContext.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS).absolutePath
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
// Javascriptを実行可能にする
this.wb!!.getSettings().javaScriptEnabled = true
// 自身を”NativeObj”としてJavascriptから参照かのうなように設定
// ただし、参照可能なメソッドは、@JavascriptInterfaceでアノテーションする必要がある。
this.wb!!.addJavascriptInterface(this, "NativeObj")
this.wb!!.loadUrl("file:///android_asset/main.html")
}
@JavascriptInterface
fun testHoge(): String {
return "test hoge!!!"
}
@JavascriptInterface
fun generatePdfOnNative(encPdf: String): String {
Log.d("native", encPdf);
// Base64エンコードで送られてくるのでデコードする
var decPdf = Base64.decode(encPdf,Base64.DEFAULT)
if (!File(this.storeDir!!).exists()) {
File(this.storeDir!!).mkdir()
}
// とりあえず、テキトーにファイル出力
val pdfPath = this.storeDir!! + "/ttt.pdf"
val fp = File(pdfPath)
var fos = FileOutputStream(fp)
var bos = BufferedOutputStream(fos)
// 出力ストリームへの書き込み(ファイルへの書き込み)
bos.write(decPdf)
bos.flush()
bos.close()
Log.d("@@@@","write pdf : " + fp.absolutePath)
// PDF描画
renderPdf(pdfPath)
return pdfPath
}
fun renderPdf(pdfPath: String ) {
// PDFはWebView上で直接表示できないので、
// ImageViewに切り替えて描画する
var fd: ParcelFileDescriptor = ParcelFileDescriptor.open(File(pdfPath), ParcelFileDescriptor.MODE_READ_ONLY)
var renderer: PdfRenderer = PdfRenderer(fd)
var page: PdfRenderer.Page = renderer.openPage(0)
var view: ImageView = findViewById(R.id.pdf_preview) as ImageView
var viewWidth: Int = view.getWidth()
var viewHeight: Int = view.getHeight()
var pdfWidth: Float = page.getWidth().toFloat()
var pdfHeight: Float = page.getHeight().toFloat()
// 縦横比合うように計算
var wRatio = viewWidth / pdfWidth;
var hRatio = viewHeight / pdfHeight;
if (wRatio <= hRatio) {
viewHeight = Math.ceil(pdfHeight.toDouble() * wRatio).toInt()
} else {
viewWidth = Math.ceil(pdfWidth.toDouble() * hRatio).toInt()
}
/
// Bitmap生成して描画
var bitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888);
page.render(bitmap, Rect(0, 0, viewWidth, viewHeight), null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
pdfBitMap = bitmap
// WebViewのJavascriptからのメソッド実行は
// UIスレッドではないらしい。
// なので、UIスレッド上で実行
var uiHandler = Handler(Looper.getMainLooper())
var run = Runnable {
view.setImageBitmap(this.pdfBitMap!!);
this.wb!!.visibility = android.view.View.INVISIBLE
view.visibility = android.view.View.VISIBLE
Log.d("@@@@@","draw")
}
uiHandler.post(run)
}
}
実行してみる
ボタンを押す。。。
出力された。
・TODO、その他。
1.
Androidから実行してるかiOSから実行してるか
JSで切り分けられるよーにする。
PDFの表示方法がWebViewでなんとかできないかどーか調べる。
容量による問題や、生成時の処理中画面ガードはiOSと同じTODO