[備忘録]アプリ上でpdfMakeにて日本語PDF生成(swift編)
・目的
haruを使わずに日本語PDFを出力したい。(日本語をテキストとして出力したい)
できれば、android側もkotolinで書いて移植しやすいよーにしたい。
・前準備
※Mac上で実行したが、多分、Windowsでも問題ない(と思いたい)
1.
bower,gruntをインストール
(nodeはインストールされている事前提)
sudo npm install -g grunt-cli
sudo npm install -g bower
bowerいらんかも。。。orz
2.
githubからソースダウンロードし解凍
https://github.com/bpampuch/pdfmake
3.解凍したフォルダにもぐり、ライブラリインストール
npm install grunt-text-replace grunt-browserify grunt-contrib-uglify grunt-dump-dir grunt-contrib-concat
npm install runt-mocha-cov grunt-jsdoc runt-contrib-jshint
4.
フリーのTrueTypeのフォントを落としてくる
とりあえず、ここから。
http://ipafont.ipa.go.jp/
で、解凍。
5.
「2.」で解凍したフォルダの examples/fontsフォルダ下の、Roboto*.ttfファイルをすべて消し、
「4.」で解凍したttfファイルを置く
(明朝を使うので、とりあえず明朝の方)
6.
「2.」で解凍したフォルダ下で、コマンド実行
grunt dump_dir
build下の「vfs_fonts.js」が、解凍したフォント用に置きかわる。
前準備終わり。
・iOS側実装
1.
xcodeでシングルビューのswiftのプロジェクト作成
2.
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/>
<div style="display:none;">
<!--
nativeactionとかいう、テキトーなプロトコルをつけ、
UIWebviewにてhandleする。
メソッドはPDFがエンコードされてダラダラ長いのでPostとする。。。
-->
<form id="frm_generate_pdf" method="post" action="nativeaction://generated_pdf">
<input type="hidden" name="enc_pdf" id="enc_pdf" value=""/>
<!-- 以下パラメータはいらんけど、とりあえず、
postのパラメータテストの為に付加した。。。 -->
<input type="hidden" name="fuga" value="tttt"/>
<input type="hidden" name="ggg" value=""/>
</form>
</div>
<script src="./jquery.min.js"></script>
<script src="./pdfmake.js"></script>
<script src="./vfs_fonts.js"></script>
<script>
var docDefinition;
// 初期化
$(function(){
pdfMake.fonts = {
tekito_font: {
normal: 'ipaexm.ttf'
}
};
$("#btn_gen_pdf").on("click",function(){
genPdf();
return false;});
docDefinition = {
content: [
{text: 'This is an sample PDF printed with pdfMake. 日本語のテスト',
style: 'tekito_style'
}],
styles: {
tekito_style: {
fontSize: 25
}
},
defaultStyle: {font: 'tekito_font'}
};
});
// PDF生成
function genPdf() {
try {
var _hoge = pdfMake.createPdf(docDefinition);
// pdfMake.createPdf().open()は
// mobileSafariでは実行されない
// なので、Base64でエンコードされた文字列を
// フォームでpostする(多分、Getはダメ。。。)
//
// getDataUrl(function)はopenの中でやってるメソッド。
// pdfをエンコードして文字列にしてる
_hoge.getDataUrl(function(result) {
// 「data:application/pdf;base64,」は
// ios上で正規表現使って置換するのは面倒くさいので、ここで除去。。。
$("#enc_pdf").val(result.replace(/^data:application\/pdf;base64,/, ''));
$('#frm_generate_pdf').submit();
});
} catch(e) {
alert(e);
}
}
// PDF表示
function handleCreatePdfFromiOS(pdfPath) {
// めんどくさいので、webviewからjavascriptキック
location.href = pdfPath;
}
</script>
</body>
</html>
プロジェクトにてきとーにフォルダを掘り、コピー
(jsファイルもコピー(「vfs_fonts.js」は、前準備で作ったフォントファイル))
3.
ストリーボード上のVCのビューにWebView貼り付けて、
constraint全画面にし、vcのソースに紐付け
4.
VCのソースをこんな感じで。。。
import UIKit
// UIWebViewDelegeteでリクエストをキャプチャする
class ViewController: UIViewController,UIWebViewDelegate {
@IBOutlet weak var wb: UIWebView!
// リクエストハンドラ
let nativeReqHandler = NativeRequestHandler()
// 起動画面
var targetURL = NSBundle.mainBundle().pathForResource("hoge", ofType: "html");
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// リクエストをキャプチャするのでWebViewのデリゲート設定
wb.delegate = self
// 起動画面をリソースに。。。
let requestURL = NSURL(string: targetURL!)
let req = NSURLRequest(URL: requestURL!)
wb.loadRequest(req)
}
// まあ、どーでもいい
override func viewDidLoad() {
super.viewDidLoad()
}
// まあ、どーでもいい
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// WebViewのデリゲートメソッド
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
// 何かリクエスト来たら
// リクエストハンドラに処理を委譲
// プロトコルで「nativeaction」が来たら、
// 処理をswift側でやる。
return self.nativeReqHandler.handleRequest(webView, request: request, naviTp: navigationType)
}
func webViewDidFinishLoad(webView: UIWebView) {
// shouldStartLoadWithRequestでfalseの場合はこのイベントは起きない
// webView.request!.URL
// print("*** load completed to:\(webView.request!.URL!)")
}
}
5.
WebViewのリクストキャプチャするクラスをこんな感じで。。。
import UIKit
class NativeRequestHandler {
func handleRequest(webView: UIWebView, request: NSURLRequest, naviTp: UIWebViewNavigationType) -> Bool {
let strUrl = request.mainDocumentURL!.absoluteString
print("start handle request:\(strUrl)")
if isNativeRequest(request) {
// Native処理の場合
handleNativeRequest(webView, request: request, naviTp: naviTp)
// 画面遷移なし
return false
}
// その他はコンテンツを画面遷移するようにtrueを返す
return true
}
// nativeaction判定
func isNativeRequest(request: NSURLRequest) -> Bool {
// 適当。。。
if request.URL!.scheme.caseInsensitiveCompare("nativeaction") == NSComparisonResult.OrderedSame ||
request.URL!.scheme.caseInsensitiveCompare("data") == NSComparisonResult.OrderedSame{
return true
}
return false
}
// Getパラメータパース
func parseQueryString(request: NSURLRequest) -> [String:String] {
let q = request.URL!.query
if q == nil {
let empty: [String:String] = [:]
return empty
}
let prms = request.URL!.query!.componentsSeparatedByString("&")
var params: [String:String] = [:]
for sprm: String in prms {
let prms = sprm.componentsSeparatedByString("=")
params[prms[0]] = prms[1]
}
return params
}
// Getパラメータパース
func parseGetParams(request: NSURLRequest) -> NSDictionary {
let strUrl = request.mainDocumentURL!.absoluteString
let urlAttr = strUrl.characters.split{$0 == "?"}.map(String.init)
let urlParamsStr: String = urlAttr[1]
let urlParamStrs = urlParamsStr.characters.split{$0 == "&"}.map(String.init)
let results = NSMutableDictionary()
for urlParamStr: String in urlParamStrs {
let keyVal = urlParamStr.characters.split{$0 == "="}.map(String.init)
results[keyVal[0]] = keyVal[1]
}
return NSDictionary(dictionary: results)
}
// JSからのデバッグ出力
func handleDebugLogFromJS(webView: UIWebView, request: NSURLRequest) {
let strUrl = request.mainDocumentURL!.absoluteString
let urlAttr = strUrl.characters.split{$0 == "?"}.map(String.init)
let urlParamsStr: String = urlAttr[1]
let encMsg = urlParamsStr.stringByReplacingOccurrencesOfString("message=", withString: "")
let data = NSData(base64EncodedString: encMsg, options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters)
let logMsg = NSString(data: data!, encoding: NSUTF8StringEncoding) as! String
print(logMsg)
}
// Post判定
func isPostMethod(request: NSURLRequest) -> Bool {
let m = request.HTTPMethod
if (m != nil) {
if m! == "POST" {
return true;
}
}
return false
}
// アプリ処理呼び出しハンドラ
func handleNativeRequest(webView: UIWebView, request: NSURLRequest, naviTp: UIWebViewNavigationType) {
let strUrl = request.mainDocumentURL!.absoluteString
var pMap = Dictionary<String,String>()
let method = request.HTTPMethod
print("method:[\(method)]")
// BodyからPostパラメータを取り出す
let data = request.HTTPBody;
if (data != nil) {
let bodyStr = NSString(data: data!, encoding: NSUTF8StringEncoding)! as String!
print("==============")
let params = bodyStr.characters.split{$0 == "&"}.map(String.init)
for param in params {
let pKv = param.characters.split{$0 == "="}.map(String.init)
if pKv.count == 1 {
pMap[pKv[0]] = ""
} else {
// Postパラメータはエンコードされてるので、デコード
var v = pKv[1]
let decodeS = (v as NSString!).stringByRemovingPercentEncoding
if decodeS != nil {
v = decodeS as String!
}
pMap[pKv[0]] = v
}
}
for (k,v) in pMap {
print("[\(k)]=[\(v)]")
}
}
// PDF出力指示の場合
// ここでやる処理じゃないけど、忘備録なので、とりあえず、ここで書く。。。
if strUrl.hasPrefix("nativeaction://generated_pdf") {
if pMap["enc_pdf"] != nil {
let decodedData = NSData(base64EncodedString: pMap["enc_pdf"]!, options: NSDataBase64DecodingOptions())
if decodedData != nil {
// Documentディレクトリを取得
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
// ファイル名
let fileName = "/ggg.pdf"
// 保存する場所
let filePath = documentsPath + fileName
// print(filePath)
decodedData!.writeToFile(filePath, atomically: true)
// print("decode")
// 画面上のjavascriptをキックし、ローカルに出力したPDFを表示。
// location.hrefしてるだけだが。。。
webView.stringByEvaluatingJavaScriptFromString("handleCreatePdfFromiOS('file://\(filePath)');")
}
}
} else {
// どうしようか。。。
}
}
}
いらない処理がたくさん入ってるけど、とりあえずこんな感じで。。。
ボタンがかぶってるけど、気にしない。。
で、ボタンを押してみる。
日本語で、PDFが出力された。
・TODO、その他。
1.
PDFをJS作る時、ちょっと考えるので、お待ちくださいが必要。
2.
作った結果をBase64エンコードするので、大きいサイズだとどうなるか。
3.
画像を使う場合、画像データはパスで指定できない。Base64でエンコードしたものを
jsonに設定する。「2.」に関連して、サイズが、どしても、大きくなるのでどーしたものか。。。
でも、テキストベースの軽いPDFならこれで、haruを導入しなくてよいし、
kotolinとあるてーど共通化出来るのでとても助かる。。。
Author And Source
この問題について([備忘録]アプリ上でpdfMakeにて日本語PDF生成(swift編)), 我々は、より多くの情報をここで見つけました https://qiita.com/cyclon2joker/items/1830313816d26e42f131著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .