안드로이드에서 PDF 다루기 - 1 (Util)

2023. 5. 5. 23:12Mobile/Android

작성자알 수 없는 사용자

728x90
반응형

안녕하세요. 기깔나는 사람들에서 안드로이드를 맡고 있는 마플입니다.
이번에는 Android에서 PDF를 다뤄 볼거에요. 

PDF 관련 Util를 한번 봐볼 까요?


안드로이드에서는 Pdf를 다루기 위해 기본적으로 SDK API에 PdfDocument가 있습니다. 해당 라이브러리로 pdf를 다뤄볼게요.

Util

1. PdfHelper

1) view를 Pdf화 

특정 view를 pdf 객체화 시킵니다.

view의 bitmap를 pdf에게 그대로 그려서 반환 합니다. 

/**
 * pdf 관련 메소드가 있는 Util
 */
public class PdfHelper {

    /**
     * converts view to a pdf.
     * 해당 view를 pdf로 반환
     *
     * @param view 전환 시킬 view
     * @return PdfDocument - 전환된 pdfDocument
     */
    public static PdfDocument convertViewToPdf(View view) {
        //PDF 생성
        PdfDocument document = new PdfDocument();
        PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(view.getWidth(), view.getHeight(), 1).create();
        PdfDocument.Page page = document.startPage(pageInfo);
        Canvas canvas = page.getCanvas();
        view.draw(canvas);
        document.finishPage(page);
        return document;
    }
	...
}

2) 앱 내부에 pdf 저장하기

pdfDocument를 받아 앱 내부 저장소에 context로 접근하여 filename으로 저장을 합니다.
output stream을 열고 쓰는 도중에 파일을 쓸 수 없거나 파일 스트림을 닫는 동안 문제가 발생한 경우 IOException이 발생할 수 있기 때문에 try-catch문으로 감쌋고 예외 처리를 위해 호출한 측에 해당 예외를 보내 줬어요.

    /**
     * 앱 내부 장소에 pdf를  저장하기
     *
     * @param context  -  context
     * @param filename - 저장할 파일 이름
     * @param document - 저장할 pdf
     * @throws IOException - outputStream error
     */
    public static void savePdfInternal(Context context, String filename, PdfDocument document) throws IOException {
        try (FileOutputStream outputStream = context.openFileOutput(filename, Context.MODE_PRIVATE)) {
            document.writeTo(outputStream);
        } catch (IOException e) {
            throw e;
        }
    }

}

3) 외부 저장소에 pdf 저장

외부 저장소의 download 디렉터리에 지정한 파일 이름으로 pdf를 저장합니다. 
File을 열고 쓰는 도중에 파일을 쓸 수 없거나 파일 스트림을 닫는 동안 문제로 인해 IOException이 발생할 수있기에 thorw를 통해 보내 줬어요.

    /**
     * save pdf into external storage(download)
     * pdf를 외부 저장소에 저장함 (downlad 폴더)
     *
     * @param filename - 저장할 파일 이름
     * @param document - target pdf
     * @throws IOException - FileStream 오류
     */
    public static void savePdfFileExternalStorage(String filename, PdfDocument document) throws IOException {
        File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        File file = new File(dir, filename);
        FileOutputStream fos = new FileOutputStream(file);
        document.writeTo(fos);
        document.close();
        fos.close();
    }

4) 앱 내부 저장소에서 pdf 가져오기

앱 내부에서 (pdf 형식의) 파일 명과 맞는 파일을 찾아 URI를 리턴해 줍니다.
uri이 null 경우는 파일 이름이 잘못되었거나 파일이 실제로 존재하지 않는 경우를 가져 올 수없는 경우이기 때문에 FileNotFoundException을 throw시켰어요.

    /**
     * 앱 내부 저장소에 해당 pdf파일이 있는지 확인
     *
     * @param context  - context
     * @param fileName - 파일 이름
     * @return Uri - 해당 파일의 Uri
     * @throws FileNotFoundException - 파일 명으로 된 파일이 없음.
     */
    public static Uri getInternalPdfFile(Context context, String fileName) throws FileNotFoundException{
        File file = new File(context.getFilesDir(), fileName.contains(".pdf") ? fileName : fileName + ".pdf");
        Uri uri = FileProvider.getUriForFile(context, "com.example.authenticationandauthorization.fileprovider", file);
        if(uri == null){
            throw new FileNotFoundException();
        }
        return uri;
    }

2. PdfRendererBasic

파일에서  pdf를 가져오는 랜더의 존재가 필요해요. 

파일을 붙잡고 있는 객체들이 있어서 close를 호출해서 해당 객체에서 파일을 풀어줘야 해요.

/**
 * Pdf 파일을 읽어서 랜더링을 도와 주는 객체
 * 쓴 이후에 close 호출 필요
 */
public class PdfRendererBasic {

    /**
     * pdf 랜더러
     */
    private PdfRenderer mPdfRenderer;
    /**
     * 현재 보고 있는 페이지 정보
     */
    private PdfRenderer.Page mCurrentPage;
    /**
     * 읽는 파일 정보
     */
    private ParcelFileDescriptor mFileDescriptor;

    ...
}

1) File을 인자로 받는 생성자

pdf 파일 정보를 읽어서 랜더를 초기화 시켜줍니다.
파일 읽는 중 파일이 존재하지 않거나 읽기 권한이 없는 경우가 있을 수 있어서 IOException을 throw 해줬어요.

    /**
     * 파일을 읽어서 pdf 랜더에게 전달
     * @param file - 읽고자 하는 파일(pdf)
     * @throws IOException - 파일 읽는 중 오류 (예: 파일이 존재하지 않거나 읽기 권한이 없는 경우)
     */
    public PdfRendererBasic(File file) throws IOException{
        try {
            mFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
            mPdfRenderer = new PdfRenderer(mFileDescriptor);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }

2) Filepath을 인자로 받는 생성자

pdf 파일 패스를 통해  파일을 열고 파일 정보를 렌더에게 넘겨서 초기화 시킵니다.

파일 읽는 중 파일이 존재하지 않거나 읽기 권한이 없는 경우가 있을 수 있어서 IOException을 throw 해줬어요.

/**
     * 파일 패스를 기준으로 읽어서 pdf 렌더에게 전달
     * @param filePath - 읽고자 하는 파일 패스(pdf)
     * @throws IOException - 파일 읽는 중 오류 (예: 파일이 존재하지 않거나 읽기 권한이 없는 경우)
     */
    public PdfRendererBasic(String filePath) throws IOException{
        try {
            mFileDescriptor = ParcelFileDescriptor.open(new File(filePath), ParcelFileDescriptor.MODE_READ_ONLY);
            mPdfRenderer = new PdfRenderer(mFileDescriptor);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }

3) InputStream 을 통해 pdf 파일을 받는 생성자

inputstream에 있는 내용을 tempFile로 만들고 그 정보를 렌더에게 전달합니다. 

임시 파일 생성에 실패하거나 입출력 작업에 문제가 발생할 경우가 있을 수 있어서 IOException을 throw 해줬어요.

/**
     * pdf를 stream에 넣은 것을 전달받아 temp file을 생성하고 pdf 렌더에게 전달
     * @param context - context
     * @param inputStream - pdf 파일이 있는 input stream
     * @throws IOException - 파일 읽는 중 오류 (예: 임시 파일 생성이 실패하거나 입출력 작업 중 문제가 발생한 경우)
     */
    public PdfRendererBasic(Context context, InputStream inputStream) throws  IOException{
        try {
            File tempFile = inputStreamToTempFile(context, inputStream);
            mFileDescriptor = ParcelFileDescriptor.open(tempFile, ParcelFileDescriptor.MODE_READ_ONLY);
            mPdfRenderer = new PdfRenderer(mFileDescriptor);
        } catch (IOException e) {
            e.printStackTrace();
            throw e;
        }
    }

4) inputStream을 통해 temp 파일을 생성

inputstream에 있는 내용을 temp 파일에 저장하고 temp파일을 리턴 해줍니다.

임시 파일 생성에 실패하거나 입출력 작업에 문제가 발생할 경우가 있을 수 있어서 IOException을 throw 해줬어요.

    /**
     * inputstream에 있는 내용을 임시 파일로 저장하여 리턴
     * @param context - context
     * @param inputStream - pdf 파일이 담긴 input stream
     * @return pdf temp file
     * @throws IOException
     */
    private File inputStreamToTempFile(Context context, InputStream inputStream) throws IOException {
        File tempFile = File.createTempFile("pdf_temp", ".pdf", context.getCacheDir());
        tempFile.deleteOnExit();

        try (OutputStream outputStream = new FileOutputStream(tempFile)) {
            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }

        return tempFile;
    }

5) 랜더링 된 특정 pdf 페이지 가져오기

pageIndex을 통해 특정 페이지에 있는 pdf 페이지의 bitmap을 리턴 해줍니다. 
입력된 값이 렌더링한 page 수보다 많은 경우 IndexOutOfBoundsException을 throw 시켜줬어요.

    /**
     * 특정 페이지에 있는 내용을 bitmap으로 바꿔서 리턴
     * @param pageIndex - 특정 페이지 인덱스
     * @return 특정 페이지에 있는 bitmap
     * @throws IndexOutOfBoundsException - 페이지 인덱스가 전체 페이지 수 보다 많음
     */

    public Bitmap renderPage(int pageIndex) throws IndexOutOfBoundsException{
        if (mPdfRenderer.getPageCount() <= pageIndex) {
            throw new IndexOutOfBoundsException();
        }

        if (mCurrentPage != null) {
            mCurrentPage.close();
        }

        mCurrentPage = mPdfRenderer.openPage(pageIndex);
        Bitmap bitmap = Bitmap.createBitmap(mCurrentPage.getWidth(), mCurrentPage.getHeight(), Bitmap.Config.ARGB_8888);
        mCurrentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);

        return bitmap;
    }

6) 렌더링 된 페이지 수 가져오기

    /**
     * 전체 페이지 수 가져 오기
     * @return 전체 페이지 수
     */
    public int getPageCount() {
        return mPdfRenderer.getPageCount();
    }

7) close

안 닫힌 자원이 있으면 닫습니다. fileDescriptor 이미 닫힌 파일을 닫으려는 경우 IOException을 잡기 위해 try-catch문으로 감싸서 처리했어요.

	/**
     * 열린 페이지, 랜더, 파일 descriptor 닫기
     * IOException - 파일 닫는 중 오류 (예: 이미 닫힌 파일을 닫으려고 하는 경우)
     */
    public void close() {
        if (mCurrentPage != null) {
            mCurrentPage.close();
        }
        mPdfRenderer.close();
        try {
            mFileDescriptor.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 


이렇게 안드로이드에서 Pdf를 다루기 위해서 Util class를 살펴보았어요. 
다음 글에서는 util을 활용하여 pdf를 화면에 보여주는 것을 해볼게요.

728x90
반응형