Some time ago, I wanted to add the function of displaying contracts in PDF in the project, but I found that Android itself did not support displaying PDF. Later, I searched on the Internet and used pdfView to display pdfView. The pdfView project package was quite large, so I used the old version to optimize it and only increased the size of my project package by about 1M. I thought it was settled in this way, but then the project manager told me: there is no electronic seal on the PDF of APP, how can the contract work without the seal….. Then I searched various materials on the Internet, but I did not find any problem about Android not showing PDF electronic seal on Baidu. Later, IOS gave me MuPDF, which was not able to display the electronic seal on IOS. I asked myself to study it. I looked for information on the Internet and found that there were only one paragraph after another. After I finished embedding, I wanted to record the process of my embedding from scratch.

I searched for integrated projects on the Internet, and found that many of them required points to download. The package I found on Github was a little too big. Later, I saw the source code and could compile it myself, so I decided to compile it myself and familiarize myself with Linux.

I am using Ubuntu and the first step is to install the NDK:

sudo apt-get install build-essentialCopy the code

2. Download and decompress NDK. I downloaded it from www.androiddevtools.cn/ and downloaded the version of 14



Then unzip it and download it in ZIP format from the above website using the command:

unzip android-ndk-r14b-linux-x86_64.zipCopy the code

If tar.gz format is used:

tar -xvf xxxxx.tar.gzCopy the code

3. Configure environment variables:

First switch to the directory decompressed by NDK, run the PWD command to query the directory address, and then copy:



Then use the following command to open the file for configuration

sudo vim ~/.bashrcCopy the code

Then configure the following parameters at the bottom of the file:

export NDK_HOME=/home/wzl/bin/andorid-ndk-r14b
export PATH=$PATH:$NDK_HOMECopy the code




Finally use the command:

ndk-build -vCopy the code

If the following information is displayed, the installation is successful:



Then we need to download the MuPDF source and compile it into the so file:

1, download decompression source code:

https://mupdf.com/downloads/ this is the source download address, and the latest is 1.12, it is the size of the 49 m, I download version 1.6, size, only 13 m (if you want to use it’s own so pack, you can download the APK and decompression, there is compiled so package, If you compile the so package with it, you can skip this section and go directly to the following sections for use in the project) :

Once the download is complete, unzip the code:

Tar - XVF mupdf - 1.6 - source. Tar. GzCopy the code

2, then go to the decompressed directory, use the following command, after the completion of the project will have a generted folder (note that this command must be executed, I did not execute the previous compilation failed)

make generateCopy the code





3. Modify the file configuration before compilation:

Go to the platform directory, there is an Android folder, go to the Android folder, the file content:

Change the sample file from local.properties.sample to local.properties and run the following command:

sudo mv local.properties.sample local.propertiesCopy the code

Open file with command:

sudo vim local.propertiesCopy the code

It is said that the SDK and NDK directories should be configured online, I only configured the NDK directory, but I later tested that it can be compiled without configuration:

ndk.dir=/home/wzl/bin/andorid-ndk-r14bCopy the code



Once configured, save and exit, then go to the jni directory and open the application. mk file (I’m using root, if not sudo) :



In it, you can choose which so package you want to compile, one at a time. The comment I open is armeabi which I need to compile:



4, compile,

Go back to the Android directory and run the dk-build command:

The first compilation takes about one minute. If the following information is displayed, the compilation is successful:



/libs/armeabi/. If you go into the directory, you should see the so package we just compiled:

If you are using a virtual machine, you can use share to share so package on Windows. If you don’t know how to share, you can click here

The compiled SO file is about 9M



Supplement:

(this supplement comes from here), this supplement can change its default package name to its own project package name, to use it may be more convenient

The package name of the so file using the above steps is com.artifex.mupdfdemo, because that is the package name of the SRC class. If we wanted to change the package name, which I have been asked, I would say no, because I wouldn't, I knew I wouldn't (% > _ < %). Now that the source code is compiled, this problem should be resolved. First let's see where com.artifex.mupdfdemo comes from. The mupdf.c file in the jni directory contains the following contents: #define JNI_FN(A) Java_com_artifex_mupdfdemo_ #define PACKAGENAME "com/artifex/ mupdfDemo" So we changed the package name here as follows: #define JNI_FN(A) Java_com_app_mupdf_ ## A #define PACKAGENAME "com/app/mupdf" Next, when using the class, change the package name of the class.Copy the code

Finally, put it into the project for use:

How to use the libmupdf.so package in the project is not described here, the details can be baidu, the following is how to use the methods in our compiled libmupdf.so package to display our PDF files.

First, if you are compiling directly without modifying the compiled package name, then you need to create a new Module in your project. The package name is com.artifex.mupdfdemo. You must use this package name unless your project has the same package name as this one. Then put the so package inside the Module. You don’t need to create a new Module project if you compile it with your own package name, just place it with the so package used in your project. The github address for the so package is github.com/muennich/mu…

Access the platform/andorid directory




Copy all the code in the circled directory. Note that the code in the res directory only needs to be copied. You can copy the code in the RES directory first, and then go to the RES to see what the project needs. Copy the code that registers Acitity into our Module



The default Activity that starts in the official code is ChoosePDFActivity, which has the following lines in the onListItemClick method:

                Uri uri = Uri.parse(mFiles[position].getAbsolutePath());
		Intent intent = new Intent(this,MuPDFActivity.class);
		intent.setAction(Intent.ACTION_VIEW);
		intent.setData(uri);Copy the code

You can see that ChoosePDFActivity is just a trivial activity, MuPDFActivity is. If you can accept the official interface, just copy the code to where your activity is going to jump. Change the parameter in the Uri to the path of our own PDF file. Note that the local file path is required. It seems that MuPDF does not support downloading PDF, so we need to download the PDF locally first, and then use MuPDF to display it. To customize the interface, the main calling code can be found in MuPDFActivity. Here is the code I used in my own project:

public class PDFActivity extends BaseActivity implements FilePicker.FilePickerSupport{

    @Bind(R.id.pageNumber)
    TextView mPageNumberView;
    @Bind(R.id.rela_layout)
    RelativeLayout mLayout;

    private String mFileName;
    private MuPDFCore core;
    private MuPDFReaderView mDocView;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pdf);
        ButterKnife.bind(this);
        setSwipeBackEnable(false);
        initData();
    }

    @Override
    protected void initData() {
        String url = getIntent().getStringExtra(Constants.KEY);
        if (!TextUtils.isEmpty(url)) {
            //判断文件是否存在  如果存在直接从文件读取  如果不存在去服务器下载
            String fileName = url.substring(url.lastIndexOf("=") + 1) + ".pdf";// 解析fileName
            final String mFile = Environment.getExternalStorageDirectory().getPath() + "/" + Environment.DIRECTORY_DOWNLOADS + "/PDFFile";
            File file = new File(mFile);
            if (!file.exists()){
                file.mkdirs();
            }
            String path = mFile +"/"+fileName;
            File filePath = new File(path);
            if (filePath.exists()){
                showPDFView(filePath);
            }else {
                showLoading();
                downloadPDF(url,path);
            }

        }
    }

    /**
     * 先下载PDF然后再设置到PDFview
     * @param mUrl
     */
    private void downloadPDF(final String mUrl,final String path) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                InputStream input = null;
                OutputStream output = null;
                try {
                    URL url = new URL(mUrl);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    Set<String> cookies = SharePerferUtil.getPreference().getCookie();
                    for (String cookie : cookies) {
                        connection.setRequestProperty(Constants.COOKIE, cookie);
                    }
                    //设置超时间为3秒
                    connection.setConnectTimeout(3 * 1000);
                    connection.setRequestMethod("GET");
                    connection.setRequestProperty("Accept-Encoding", "identity");
                    connection.connect();
                    input = new BufferedInputStream(connection.getInputStream());
                        output = new FileOutputStream(path);
                        byte data[] = new byte[1024];
                        int count;
                        while ((count = input.read(data)) != -1) {
                            output.write(data, 0, count);
                        }
                        final String code = parseJsonResultByBytes(data);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                try {
                                    if (TextUtils.isEmpty(code)){
                                        File file = new File(path);
                                        showPDFView(file);
                                    }else {
                                        //code不为空,说明请求PDF失败
                                        showInfo("未登录!请先登录");
                                    }
                                    hideLoading();
                                }catch (Exception e){}

                            }


                        });
                } catch (Exception e) {
                    e.printStackTrace();
                }finally {
                    if (output != null){
                        try {
                            output.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    if (input != null){
                        try {
                            input.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }).start();
    }

    /**
     * 显示PDF文件
     * @param file
     */
    private void showPDFView(File file) {
        Uri uri = Uri.parse(file.getAbsolutePath());
        core = openFile(Uri.decode(uri.getEncodedPath()));
        if (core == null){
            showInfo("显示PDF失败");
            return;
        }

        mDocView = new MuPDFReaderView(PDFActivity.this) {
            @Override
            protected void onMoveToChild(int i) {
                if (core == null)
                    return;
                mPageNumberView.setText(String.format("%d / %d", i + 1,
                        core.countPages()));
                super.onMoveToChild(i);
            }

            @Override
            protected void onTapMainDocArea() {
                if (mPageNumberView.getVisibility() == GONE){
                    mPageNumberView.setVisibility(VISIBLE);
                }else {
                    mPageNumberView.setVisibility(GONE);
                }
            }
        };
        mDocView.setAdapter(new MuPDFPageAdapter(PDFActivity.this, PDFActivity.this, core));
        mPageNumberView.setText(String.format("%d / %d", 1, core.countPages()));
        mLayout.addView(mDocView);
    }

    /**
     * 打开PDF文件
     * @param path
     * @return
     */
    private MuPDFCore openFile(String path)
    {
        int lastSlashPos = path.lastIndexOf('/');
        mFileName = new String(lastSlashPos == -1
                ? path
                : path.substring(lastSlashPos+1));
        System.out.println("Trying to open "+path);
        try
        {
            core = new MuPDFCore(this, path);
            // New file: drop the old outline data
            OutlineActivityData.set(null);
        }
        catch (Exception e)
        {
            System.out.println(e);
            return null;
        }
        return core;
    }


   

    //将byte解析成json
    private String parseJsonResultByBytes(byte[] bytes){
        String jsonString = getStringByBytes(bytes);
        try {
            JSONObject obje = new JSONObject(jsonString);
            String code = obje.optString("code");
            return code;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    //读取响应头
    private int getResponseHeaderStatus(HttpURLConnection conn) {
        Map<String, List<String>> responseHeaderMap = conn.getHeaderFields();
        int size = responseHeaderMap.size();
        int status = -1;
        for(int i = 0; i < size; i++){
            String responseHeaderKey = conn.getHeaderFieldKey(i);
            if (responseHeaderKey.equals("X-Android-Response-Source")){
                String responseHeaderValue = conn.getHeaderField(i);
                int index = responseHeaderValue.indexOf(" ");
                String value = responseHeaderValue.substring(index+1);
                status = Integer.parseInt(value);
                Log.e("TAG",value);
                break;
            }
//            sbResponseHeader.append(responseHeaderKey);
//            sbResponseHeader.append(":");
//            sbResponseHeader.append(responseHeaderValue);
//            sbResponseHeader.append("\n");
        }
        return status;
    }


    //根据字节数组构建UTF-8字符串
    private String getStringByBytes(byte[] bytes) {
        String str = "";
        try {
            str = new String(bytes, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return str;
    }
    @Override
    protected void addListener() {}


    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void performPickFor(FilePicker picker) {

    }
}
Copy the code