[備忘録]NDKにてHARUで日本語PDFを出力してみる


[背景]
忘れるのでメモ。
Andoiroid上でJavascriptで日本語PDFを生成していたが、NDKを使ってみたかったのと、
Android上で、フリーのピュアJavaの日本語PDF生成ライブラリが見つけられなかった為。
(ApacheのPDFのライブラリのAndroidで動くよう調整したものが、github上に公開されているが、
調整元のバージョンが古いので日本語に対応していない)


[前提]
・androidstudioに、NDKのbuild環境を導入済み
・macにはcmakeを導入済み
・zlibはAndroidのものを使用
・pnglibは、githubにあるAndroid用に調整されたソースを使用
（設定がわからなかったので、自分でconfigureできなかった。。。）
・haruは、githubのソースを使用
・実行環境は5.1以上とする
（別に拘りはないが、持ってる実機で一番古いのが5.1の為。。。）
・ソースはutf8、出力するPDFはsjis(HARUの日本語出力が、sjisかeucの為)
・生成したPDFの確認はIntent経由でAcrobatで表示する。なので、最終的には実機で確認する(とーぜん、Acrobatはインストール済み)



[前準備]
1.日本語フォントのDL
IPAのttfを使うことにした。
とりあえず、IPA明朝を取得。
https://ipafont.ipa.go.jp/old/ipafont/download.html
適当なフォルダに解凍しておく。

2.HARU
githubの方から取得した。
https://github.com/libharu/
適当なフォルダに解凍しておく。


3.libpng
HARUに必要。(libpngはzlib依存だが、androidのzlibを使用する)
android用に調整されたものが、githubに置かれているので、
それを取得した。
https://github.com/julienr/libpng-android
適当なフォルダに解凍しておく。



[プロジェクトの生成]

1.androidstudioのIDEを開き、プロジェクトの新規作成する。

![prj-gen-3.png](https://qiita-image-store.s3.amazonaws.com/0/38889/a501424f-b59a-500b-3cbd-95518683d746.png "prj-gen-3.png")

C++サポートのチェックはこの段階では要らない。

![prj-gen-3.png](https://qiita-image-store.s3.amazonaws.com/0/38889/c184ed1e-596d-7d4a-8962-e0d60bc7ca05.png "prj-gen-3.png")

5.1の実機があるので、5.1にした。。。

![prj-gen-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/55c4bce2-2d88-f190-d79c-d99105040a72.png "prj-gen-1.png")

とりあえず、emptyで生成。
あとは、デフォルトのまま。

![prj-gen-4.png](https://qiita-image-store.s3.amazonaws.com/0/38889/4d8b257e-3291-e9f8-5701-abe1efed77ba.png "prj-gen-4.png")

生成直後。。。

プロジェクトのappフォルダを開き、jniフォルダを作成しておく

![prj-gen-5.png](https://qiita-image-store.s3.amazonaws.com/0/38889/128d831e-f035-5393-6141-4035ee0e4c21.png "prj-gen-5.png")




[Cソースの準備]

1.前述jniフォルダに、libpngフォルダを作成
![csrc-libpng-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/cd3135df-f5eb-f388-5a6c-af890c704a25.png "csrc-libpng-1.png")


2.libpngの解凍ファイル内のjniフォルダの中身を前述libpngフォルダにコピー。
ただし、「Android.mk」、「Application.mk」 は不要。
（armフォルダとその中身、c,hソースをコピーする）


![csrc-libpng-2.png](https://qiita-image-store.s3.amazonaws.com/0/38889/fe9b9954-4d12-c004-e95a-5136844c136d.png "csrc-libpng-2.png")


3.前述jniフォルダにlibharuフォルダ作成
![csrc-libpng-3.png](https://qiita-image-store.s3.amazonaws.com/0/38889/dbd7752d-4ef3-663d-4fdd-053d2e12609f.png "csrc-libpng-3.png")


4.libharuの解凍ファイル内のjniフォルダの中身を前述libharuフォルダにコピー。
（不要なファイルもあるが、とりあえず、そのままコピーする）

![csrc-libharu-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/9788ef74-d849-a6fb-e297-d010ff77b3c2.png "csrc-libharu-1.png")

5.コンソールを起動し、libharuフォルダに移動。
フォルダ直下で、cmake実行

![csrc-libharu-2.png](https://qiita-image-store.s3.amazonaws.com/0/38889/95c1141e-0497-3e7c-addb-1066f73f4249.png "csrc-libharu-2.png")


![csrc-libharu-3.png](https://qiita-image-store.s3.amazonaws.com/0/38889/e4f431aa-87db-a48c-0f3d-9ac4aa651def.png "csrc-libharu-3.png")


```
cmake ./
```

6.前述jniフォルダにharu_wrapperフォルダ作成
(HARUにjniでアクセスする関数を作成するソースの格納フォルダ)

![csrc-hwr-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/f413e4e9-0512-ea16-7308-31766bd3876a.png "csrc-hwr-1.png")

7.haru_wrapperフォルダ下で、ソース、「native-test.cpp」を新規作成し、
以下のように記述。
![csrc-hwr-2.png](https://qiita-image-store.s3.amazonaws.com/0/38889/63e9f99d-3fba-1c3f-b770-e92e1bfd1a6d.png "csrc-hwr-2.png")

```
#include <jni.h>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#include <android/log.h>

// haru-lib include
#include "hpdf.h"


// JNI初めてのよびだし。。。　
extern "C" JNIEXPORT jstring
JNICALL
Java_gkgkdrink_com_fugaharu_MainActivity_stringTest(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from First C++!";
    return env->NewStringUTF(hello.c_str());
}


```

※疎通確認のため、とりあえず、jni経由で文字列を返すだけの関数を追加。


8.前述jni下フォルダに「Android.mk」、「Application.mk」ファイルを新規作成。

![csrc-mk-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/b6efcd16-0ef6-efca-5f21-a12ccc95b074.png "csrc-mk-1.png")

9.「Android.mk」を以下のように記述して保存

```
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libpng
LOCAL_SRC_FILES := $(subst $(LOCAL_PATH)/,, $(wildcard $(LOCAL_PATH)/libpng/*.c)) \
                   $(subst $(LOCAL_PATH)/,, $(wildcard $(LOCAL_PATH)/libpng/arm/*.c))
LOCAL_EXPORT_LDLIBS := -lz
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/libpng
LOCAL_MODULE     := png

APP_STL:=gnustl_static

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := \
    $(LOCAL_PATH)/libpng \
    $(LOCAL_PATH)/libharu/include \
    $(LOCAL_PATH)/libharu/src \
    $(LOCAL_PATH)/haru_wrapper
LOCAL_SRC_FILES := $(subst $(LOCAL_PATH)/,, $(wildcard $(LOCAL_PATH)/libharu/src/*.c))
LOCAL_LDLIBS    := -lz -lm -llog
LOCAL_CFLAGS    += -O2  -D__ANDROID__ -fPIC
LOCAL_MODULE    := haru

include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)

LOCAL_C_INCLUDES := \
    $(LOCAL_PATH)/zlib \
    $(LOCAL_PATH)/libpng \
    $(LOCAL_PATH)/libharu/include \
    $(LOCAL_PATH)/libharu/src \
    $(LOCAL_PATH)/haru-wrapper
LOCAL_SRC_FILES := $(subst $(LOCAL_PATH)/,, $(wildcard $(LOCAL_PATH)/haru-wrapper/*.cpp))
LOCAL_LDLIBS    := -lz -lm -llog
LOCAL_CFLAGS    += -O2  -D__ANDROID__ -fPIC
LOCAL_MODULE    := fuga

ifeq ($(TARGET_ARCH_ABI),$(filter $(TARGET_ARCH_ABI), arm64-v8a mips64 x86_64))
LOCAL_CFLAGS    += -D__ANDROID64__
endif

LOCAL_STATIC_LIBRARIES := png haru

include $(BUILD_SHARED_LIBRARY)
```

10.「Applicatio.mk」を以下のように記述して保存

```
APP_PLATFORM := android-22
APP_ABI := armeabi armeabi-v7a x86 mips arm64-v8a x86_64 mips64
APP_STL:=stlport_static
APP_MODULES := fuga
```


[NDKのビルド設定と疎通確認]

1.androidstudioのプロジェクトペインで、「app」を選択して右クリックし、
メニューから「Link C++ Project with Gradle」を選択し、実行。

![firstndk-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/8041f845-4185-e594-7903-a0a64ca2cbe1.png "firstndk-1.png")

2.ダイアログが表示されるので、

![firstndk-2.png](https://qiita-image-store.s3.amazonaws.com/0/38889/ee930c80-3d46-2393-2e60-34577b7b988b.png "firstndk-2.png")

「Build System」-> 「ndk-build」
「Build System」-> 作成した「Android.mk」のパス

を選択し、「OK」ボタンをクリック。
ビルドが始まる。
正常に終わる（ハズ。。。）なので、おわると、プロジェクトペインは
以下のようになる。

![firstndk-3.png](https://qiita-image-store.s3.amazonaws.com/0/38889/3b6d7b76-1bf4-1558-44bb-f741e6a1a83b.png "firstndk-3.png")


Cのフォルダと、Cのmakeファイルが、現れる。

「build.gradle」も、

```
    externalNativeBuild {
        ndkBuild {
            path 'jni/Android.mk'
        }
    }
```

が、追記されている。（ハズ。。。）

3.疎通確認のため用意したtestString()を呼び出す為、Java側ソースを編集する。

デフォルトでプロジェクトを作成しているので、レイアウトにはラベルが存在するはずなんで、
それにidを付与する。
layout/activity_main.xml

```
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/txtFuga"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

```


Activity表示時に、jni経由で文字列を取ってきてラベルに表示する。
gkgkdrink.com.fugaharu.MainActivity.java


```
package gkgkdrink.com.fugaharu;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    // HaruのラッパーLib
    static {
        System.loadLibrary("fuga");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // jni疎通確認。。。
        TextView txt = findViewById(R.id.txtFuga);
        txt.setText(this.stringTest());
        
    }

    // 疎通確認用native関数
    public native String stringTest();

}


```

staticライブラリのfugaをロード、stringTest()をnativeメソッドとして定義し、画面ロード時にラベルに設定するよう記述した。

クリーンしてリビルド後、エミュレータで実行してみる。

![firstndk-4.png](https://qiita-image-store.s3.amazonaws.com/0/38889/973dcb0c-7e2f-8604-773e-15200acfcda3.png "firstndk-4.png")

レイアウトに設定されている「Hello Wold!」から「Hello from First C++!」が表示されいるので、
Cのライブラリの疎通が確認出来た。

[日本語PDFの出力]

1.プロジェクトにダウンロードしたIPAの明朝フォントを取り込む。

finder等でプロジェクトの「app/src/main」フォルダを開き、「assets」フォルダを作成する。
![font-add-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/d2c9bbfd-1692-1a6e-c995-18d8052f1509.png "font-add-1.png")


assetsフォルダに解凍した明朝ttfファイルをコピー

![font-add-2.png](https://qiita-image-store.s3.amazonaws.com/0/38889/25d7e3bd-b00c-c807-5f6f-676ac3734ccb.png "font-add-2.png")

androidstudioに戻ると、assetsがプロジェクトペインにある
（ハズ。。。なければ、リビルドするか、Sync With FileSystemする。。。）

![font-add-3.png](https://qiita-image-store.s3.amazonaws.com/0/38889/9a0a28a3-d480-1fa0-a2bd-c2aa08391e50.png "font-add-3.png")


HARUは、外部フォントをパスを指定してロードする。
この為、assetsに置いただけでは使えない。
(Streamで取れる為。ここら辺のクソ仕様はなんとかならんものか。。。)
なので、ローカルフォルダにフォントをコピーするコードをJava側で記述する。

「build.gradle」に以下の設定を追加する。
（前述、externalNativeBuildの上らへん。。。）

```

    aaptOptions {
        noCompress "ttf"
    }


```
※これをしておかないと、ローカルにコピーした時に、
「java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed」
が発生するという、クソ仕様。。。

「AndroidManifest.xml」にアクセス権限を追加する。

```
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="gkgkdrink.com.fugaharu">

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <application

```

MainActivityに以下処理を追加

```

//〜省略〜

        // jni疎通確認。。。
        TextView txt = findViewById(R.id.txtFuga);
        txt.setText(this.stringTest());
        
        // Font準備
        String fontDir = this.getApplicationContext().getExternalFilesDir(null) + "/fonts/";
        File fontDirFp = new File(fontDir);
        if (!fontDirFp.exists()) {
            fontDirFp.mkdir();
        }
        File font1Fp = new File(fontDirFp.getAbsolutePath() + "/ipaexm.ttf");
        if (!font1Fp.exists()) {
            AssetManager am = getApplicationContext().getAssets();
            AssetFileDescriptor afd = null;
            try {
                if (!font1Fp.exists()) {
                    afd = am.openFd( "ipaexm.ttf");
                    Log.i("FUGA", "copy start ipaexm.ttf");
                    copyFile(afd, font1Fp);
                    Log.i("FUGA", "copy success ipaexm.ttf");
                } else {
                    Log.i("FUGA", "font ipaexm.ttf exists.");
                }
            } catch (Exception e) {
                Log.e("FUGA", "font load failed...", e);
            }
        }

//〜省略〜

    private void copyFile(AssetFileDescriptor src, File dst) throws Exception {
        byte[] buf = new byte[1024];
        long len = 0;
        InputStream is = null;
        FileOutputStream os = null;
        try {
            is = src.createInputStream();
            os = new FileOutputStream(dst);
            // ファイルの終わりまで読み込む
            while((len = is.read(buf)) != -1){
                os.write(buf);
            }
            os.flush();
        } finally {
            if (is != null) {
                is.close();
            }
            if (os != null) {
                os.close();
            }
        }
    }

//〜省略〜

```

アプリの内部ストレージにフォントフォルダを作成し、
assetsのttfをコピーしているだけ。
とりあえず、エミュレーターで確認。

```
05-12 08:03:25.431 31258-31258/gkgkdrink.com.fugaharu I/FUGA: copy success ipaexm.ttf
```

ローカルに実ファイルとして、コピーした事を確認。。。

2.C側に文字列SJIS変換関数を用意する。
前述のとおり、HARUはShift_JIS、もしくは、EUC-JPしか受け付けない。
Android側から送られる文字列は、当然、UTF-8の為、変換が必要になる。そこで、C側にSJIS変換関数を、native-test.cppに用意した。

```

// 〜中略

// SJIS変換関数
char* GetStringLocalChars(JNIEnv *env, jstring str){

    jstring enc = env->NewStringUTF("Shift_JIS");

    jclass clazz = env->FindClass("java/lang/String");
    jmethodID getBytes = env->GetMethodID(clazz, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray bytes = (jbyteArray)env->CallObjectMethod(str, getBytes, enc);

    jsize len = env->GetArrayLength(bytes);
    char *s = (char*)malloc(len+1);

    jbyte *bs = env->GetByteArrayElements(bytes, NULL);
    memcpy(s, bs, len);

    env->ReleaseByteArrayElements(bytes, bs, 0);

    s[len] = '\0';
    return s;
}

// 文字列のメモリ解放
void ReleaseStringLocalChars(char* str){
    free(str);
}

// 〜中略


```

処理内容は、JavaのStringをsjisで作り直して、getBytesメソッドで取り出し、コピーしているだけ。ただし、mallocしているので、使ったら解放が必要になる。


3.C側にHARUにてPDFを生成するラッパー関数を用意する。

エラーハンドラを用意。

```

// 〜中略

// エラーハンドラ
#ifdef HPDF_DLL
void  __stdcall
#else
void
#endif
error_handler (HPDF_STATUS   error_no,
               HPDF_STATUS   detail_no,
               void         *user_data)
{
    __android_log_print(ANDROID_LOG_ERROR, "FUGA", "ERROR: error_no=%04X, detail_no=%u\n", 
                        (HPDF_UINT)error_no,
                        (HPDF_UINT)detail_no);
}

// 〜中略

```

特に、何もしない。。。メッセージを表示するのみとした。

次にPDF出力関数。

```

// 〜中略

// 日本語PDFを出力
extern "C" JNIEXPORT void JNICALL Java_gkgkdrink_com_fugaharu_MainActivity_fugaPdf(
        JNIEnv *env,
        jobject,
        jstring pdf_path,
        jstring fontpath,
        jstring writemsg) {

    HPDF_Doc  pdf;
    HPDF_Font font;
    HPDF_Page page;
    HPDF_STATUS status;

    const char *detail_font_name;
    jboolean isCopy;


    // PDFドキュメントの作成
    pdf = HPDF_New (error_handler, NULL);
    if (!pdf) {
        printf ("error: cannot create PdfDoc object\n");
        return ;
    }

    // 日本語エンコーディングを使用する
    status = HPDF_UseJPEncodings(pdf);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf usejpencodings result %lu", status);


    // Fontパス変換
    const char *font_path = env->GetStringUTFChars(fontpath,&isCopy);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","load font start ");

    // 生成PDFへフォントを設定
    detail_font_name = HPDF_LoadTTFontFromFile(pdf, font_path, HPDF_TRUE);
    if (strlen(detail_font_name) > 0) {
        __android_log_print(ANDROID_LOG_DEBUG,"TAG","load font :[%s]", detail_font_name);
    }
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","load font end");

    /* set compression mode */
    status = HPDF_SetCompressionMode (pdf, HPDF_COMP_ALL);

    /* create default-font */

    // Fontを取得
    font = HPDF_GetFont (pdf, detail_font_name, "90msp-RKSJ-H");
    if (font == NULL) {
        __android_log_print(ANDROID_LOG_DEBUG,"TAG","get font failed ");
    } else {
        __android_log_print(ANDROID_LOG_DEBUG,"TAG","get font success.. ");
    }

    // 新規ページ作成
    page = HPDF_AddPage (pdf);
    // 新規ページは、A4縦とする
    status = HPDF_Page_SetSize(page,
                               HPDF_PAGE_SIZE_A4 ,
                               HPDF_PAGE_PORTRAIT);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf setsize result %lu", status);

    // 引数で渡された出力文字列をSJISに変換する
    char *helloMsg = GetStringLocalChars(env, writemsg);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","hello msg %s", helloMsg);

    // 出力するフォントのサイズを指定
    status = HPDF_Page_SetFontAndSize (page, font, 24);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf setfontsize result %lu", status);

    // 出力文字から幅を見積もる
    float tw = HPDF_Page_TextWidth (page, helloMsg);

    // ページへ文字列出力開始
    status = HPDF_Page_BeginText (page);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf begin result %lu", status);

    // 日本語文字の出力
    HPDF_Page_TextOut (page, (HPDF_Page_GetWidth(page) - tw) / 2,
                       HPDF_Page_GetHeight (page) - 50, helloMsg);

    // SJIS文字列の解放
    ReleaseStringLocalChars(helloMsg);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","free alloc pdf msg...");

    // ページ終了
    HPDF_Page_EndText (page);

    // 指定パスへPDFを出力
    const char *save_path = env->GetStringUTFChars(pdf_path, &isCopy);
    status = HPDF_SaveToFile(pdf, save_path);

    __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf write path %s", save_path);
    __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf write result %lu", status);

    // 結果チェック
    switch (status)
    {
        case HPDF_OK:
            __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf write result success");
            break;
        case HPDF_INVALID_DOCUMENT:
            __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf write result invalid document");
            break;
        case HPDF_FAILD_TO_ALLOC_MEM:
            __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf write result failed alloc mem");
            break;
        case HPDF_FILE_IO_ERROR:
            __android_log_print(ANDROID_LOG_DEBUG,"TAG","pdf write result io error");
            break;
        default:
            break;
    }

    // PDF解放
    HPDF_Free (pdf);
    return ;
}

// 〜中略

```

引数は、

PDF出力ファイルパス、
明朝フォントファイルパス、
出力文字列（日本語混入）

とした。エラー処理は特に実装していない。
リビルドすると、何事もなく終了する（ハズ。。。）


4.Java側にラッパー関数を呼び出す処理を「MainActivity.java」に、追加する。

ネイティブ関数呼び出しメソッドの宣言。

```

// 〜中略

    // 日本語PDFを出力
    public native void fugaPdf(String pdf_path, String fontpath, String writemsg);

// 〜中略

```

メソッドのインターフェースは、前述のとおり。

日本語PDF作成処理の追加。
追加箇所は、「onCreate()」の続きで行った。
テスト出力なので、特に非同期とかにはしない。。。

```

// 〜中略

        // PDF出力フォルダの準備
        String pdfDir = this.getApplicationContext().getExternalFilesDir(null)
                + "/share_pdf/";
        File fp = new File(pdfDir);
        if (!fp.exists()) {
            fp.mkdir();
            Log.i("FUGA", pdfDir + " created...");
        } else {
            Log.i("FUGA", pdfDir + " exists.");
        }
        // 出力先PDFパス
        String pdfPath = pdfDir + "hogehoge.pdf";
        
        // PDFに出力する日本語付きメッセージ
        String jpmsg = "HARUで日本語出力orz";

        // 出力実行
        this.fugaPdf(pdfPath, font1Fp.getAbsolutePath(), jpmsg);
        Log.d("FUGA","write pdf:" + pdfPath);

// 〜中略

```

リビルドし、エミュレーターで実行してみる。

```

05-13 09:05:42.281 4670-4670/gkgkdrink.com.fugaharu D/FUGA: write pdf:/storage/emulated/0/Android/data/gkgkdrink.com.fugaharu/files/share_pdf/hogehoge.pdf

```

正常に終了する。。。

画面を追加するのがメンド臭いので、ここからは、実機で実行する。
作成したPDFをIntent経由で実機にインストールされているAcrobatで表示し、
日本語が出力されるか確認する。

```

// 〜中略

        // 外部アプリに渡すURIを生成
        File pdfFp = new File(pdfPath);
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri uri = FileProvider.getUriForFile(this,
                "gkgkdrink.com.fugaharu.fileprovider", pdfFp);

        // Intent起動...
        intent.setDataAndType(uri, "application/pdf");
        intent.addFlags(
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivityForResult(intent, 0/*request code*/);
        Log.d("FUGA","pdf first test");
        
// 〜中略

```

URI作成時にFileProviderを使用するので、AndroidManifest.xmlに、設定を追加する。

```

// 〜中略

        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="gkgkdrink.com.fugaharu.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

    </application>

```

「application」タグ内に記述する。file_pathsが無いので新規に作成する(res/xml/file_paths.xml)


```

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="share_pdf" path="Android/data/gkgkdrink.com.fugaharu/files/share_pdf" />
</paths>


```

リビルドして、実機で実行する。

![result-app-1.png](https://qiita-image-store.s3.amazonaws.com/0/38889/958b0aa5-3b89-d8dd-4357-da1d7babdd68.png "result-app-1.png")


アプリ起動時、インテントで他アプリを起動しようとする。
ので、Acrobatを選択して、起動する。


![result-app-2.png](https://qiita-image-store.s3.amazonaws.com/0/38889/f6e3f794-8407-273c-c7af-ad8aacb45580.png "result-app-2.png")


設定した日本語付きメッセージが、PDFへ出力されていることが
確認出来る。


[まとめ]
とりあえず、NDKでビルドする、日本語PDFを出力する的な事は、
メンド臭い感じで出来る事が確認出来た。
SJIS変換とか、フォント渡すところとか、ほんとにメンド臭い。。。

公開しているアプリはwebview-javascriptで無理やりPDFを生成してるので、
これで書き直すと思う。

以下、TODO....
TODO：kotlinから呼び出すよう、書き直す。
TODO：ReactNativeとか、Fultter経由だとどう実装するか。。。