[메모] 응용 프로그램에서 pdfMake를 통해 일본어 PDF(swift 편) 생성

12497 단어 SwiftpdfMakeiOS
목적
Haru를 사용하여 일본어 PDF를 출력하지 않으려면 합니다.(일본어를 텍스트로 출력하려면)
가능하다면 안드로이드 측도 코톨린으로 써서 이식이 더욱 쉬워질 것이라고 생각합니다.
사전 준비
※ Mac에서 실행됐지만 Windows도 괜찮을 것 같습니다(제 생각엔)
1.
bower, grunt 설치
(node가 설치되어 있어야 함)
sudo npm install -g grunt-cli
sudo npm install -g 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.
무료 트루타입 글꼴 삭제
어쨌든 여기서부터 시작하자.
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.
JS로 pdf 생성 준비)
<!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 붙여넣기
전체 화면, 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.
이런 느낌으로 웹뷰 채용 포착...
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 {
        // どうしようか。。。
        }
    }

}

필요 없는 처리가 많이 들어갔지만 어쨌든 그런 느낌...
6.
실행해봐...

단추를 끼고 있지만 개의치 않는다.
그리고 버튼을 눌러주세요.

PDF를 일본어로 출력했습니다.
・토도, 기타.
1.
PDF JS를 만들 때는 고민이 필요하니 잠시만 기다려주세요.
2.
기본 코딩 64로 만들어졌기 때문에 사이즈가 크면 어떨까.
3.
그림을 사용할 때, 그림 데이터는 경로를 통해 지정할 수 없습니다.Base64에서 인코딩된 것
json으로 설정합니다.「2.」이와 관련하여 사이즈가 어떻게든 커질 수 있으니 어떻게 할까요...
단, 텍스트 기반의 가벼운 PDF라면 Haru를 가져오지 않을 수도 있습니다
코톨린과 공통점이 있지만 아주 좋은 도움이 될 수 있습니다...

좋은 웹페이지 즐겨찾기