목차

들어가며

1. 이 글의 목적

PDF Document API를 이용하여 간단하게 PDF를 만드는 방법에 대하여 알아보자.

2. 용지의 크기

PDF 문서를 만들려면 가장 먼저, 용지의 크기를 정해야 한다. 이는 당연할 것이다. 포토샵으로 따지면 캔버스의 크기를 말한다.

통상 우리가 문서를 만들때에는 A4용지를 쓴다. 그런데 A4용지의 크기는 다음과 같다.

가로 210mm = 8.26Inch 
세로 297mm = 11.69Inch

그런데 안드로이드에서 제공하는 PDF Document의 단위는 1포인트인데, 이 포인트는 1인치를 기준으로 72포인트라고 한다. 따라서 용지의 크기를 정할 떄에는 인치를 기준으로 포인트를 정하는 것이 좋다. 따라서 위의 인치를 기준으로 한 A4 용지의 크기를 포인트로 환산하면 다음과 같다.

가로 : 8.26Inch = 594.72point ≒ 595point
세로 : 11.69Inch = 841.68point ≒ 842point 

A4용지의 크리는 인치를 기준으로 하면 정확하게 포인트가 정수형으로 떨어지지 않는다. 따라서 반올림을 하여 가로 595포인트, 세로 824포인트로 정하면 A4용지의 크기를 정할 수 있다.

/**Dimension For A4 Size Paper (1 inch = 72 points)**/
val PDF_PAGE_WIDTH = 595 //8.26 Inch
val PDF_PAGE_HEIGHT = 842 //11.69 Inch

Starting Page

용지 중앙에 글자를 그리는 예시는 다음과 같다.

fun generatePDFStart()
{
    /**Dimension For A4 Size Paper (1 inch = 72 points)**/
    val PDF_PAGE_WIDTH = 595 //8.26 Inch
    val PDF_PAGE_HEIGHT = 842 //11.69 Inch
 
    // creating a PDF Document instance
    val pdfDocument: PdfDocument = PdfDocument()
 
    // Page Information : Page's Width, Height, and PageNumber
    val myPageInfo: PdfDocument.PageInfo? =
        PdfDocument.PageInfo.Builder(PDF_PAGE_WIDTH, PDF_PAGE_HEIGHT, 1).create()
 
    // Page
    val myPage: PdfDocument.Page = pdfDocument.startPage(myPageInfo)
 
    // creating a variable for canvas from our page of PDF.
    val canvas: Canvas = myPage.canvas
 
    // Variable to make a drawing(Text)
    val title: Paint = Paint()
    // Draw Text
    canvas.drawText("Hello, PDF Document!", 209F, 100F, title)
 
    pdfDocument.finishPage(myPage)
 
    savePdfFileExternalStorage("StartPDF.pdf", pdfDocument)
 
    pdfDocument.close()
}

PDF Font Change

1. Paint API

PDFDocument API에서는 글자나 그림이나 모두 android.graphics.Paint API(이하에서는 Paint API로 약칭하겠다)를 사용한다.

제트팩컴퍼즈에서 사용하는 text API는 사용할 수 없으니, Paint API를 사용하여 텍스트를 그려야 한다.

나는 고령딸기체떡볶이체를 폰트로 사용할 것이다. 따라서 다음과 같이 두개의 Paint object를 만든다.

    // Paint object to make a Text
    val title: Paint = Paint()  // Title paint, Goryoengstrawberry font
    val ttoppokki : Paint = Paint()  // text in the center of page, ttoppokki font

2. 폰트 불러오기

위의 페인트 오브젝트에서 사용할 폰트는 다음과 같이 불러온다.

    // Loading Strawberry font
    val fontStrawberry = ResourcesCompat.getFont(context, R.font.goryeongstrawberry)
    떡볶이체 폰트 불러오기
    val fontTtoppoki = ResourcesCompat.getFont(context, R.font.ttoppokki)

여기서 R.font다음에 있는 폰트리소스는 ui.theme폴더에 있는 fontfamily.kt파일에 설정된 것을 그대로 불러올 수 있다.

이를태면 나는 ui.theme폴더에 다음과 같이 fontfamily.kt를 만들어뒀다.

fontfamily.kt
val fontGoryeong = FontFamily(
    Font(R.font.goryeongstrawberry, FontWeight.Normal),
)
 
 
val ttoppokki = FontFamily(
    Font(R.font.ttoppokki, FontWeight.Normal)
)

3. 페인트 오브젝트에 폰트리소스 연결하기

페인트 오브젝트에 폰트를 연결하는 함수는 setTypeface이다. 다음과 같이 하면 된다.

title.setTypeface(fontStrawberry)  // strawberry font
ttoppokki.setTypeface(fontTtoppoki)  // ttoppoki font 

4. 컬러, 텍스트 크기, 텍스트 정렬

다음과 같은 방식으로 Paint오브젝트의 컬러, 크기, 정렬을 할 수 있다.

    // title paint setting
    title.setTypeface(fontStrawberry)
    title.color = ContextCompat.getColor(context, R.color.orange_80)
    title.textSize = 16f
 
    // ttoppokki paint setting
    ttoppokki.setTypeface(fontTtoppoki)
    ttoppokki.color = ContextCompat.getColor(context, R.color.black)
    ttoppokki.textSize = 12f
    ttoppokki.textAlign = Paint.Align.CENTER

여기서 color는 res 폴더 내에 colors.xml로 정의되어 있는 컬러코드를 말한다. ui.theme에 정의되어 있는 Color.kt가 아님에 유의하자.

참고로 나는 colors.xml을 다음과 같이 정의하였다.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="orange_80">#FFEFB8C8</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
</resources>

클래스화 하기

1. PDF Util Class

지금까를 정리하면 다음과 같이 텍스트를 만드는 기능들을 클래스화 할 수 있을 것이다.

PDFUtil.kt
class PDFUtil constructor(val context: Context) {
 
    /**Dimension For A4 Size Paper (1 inch = 72 points)**/
    val PDF_PAGE_WIDTH = 595 //8.26 Inch
    val PDF_PAGE_HEIGHT = 842 //11.69 Inch
 
    /* Font Resource */
    val fontStrawberry = ResourcesCompat.getFont(context, R.font.goryeongstrawberry)  // Strawberry font
 
 
    // A4 Size Page Info
    val A4 = a4Paper()
 
    // Paint object to make a Text
    val title: Paint = Paint()  // Title paint, Goryoengstrawberry font
 
 
    // A4 Size Paper Initializer
    private fun a4Paper() : PdfDocument.PageInfo {
 
        // Page Information : Page's Width, Height, and PageNumber
        val myPageInfo: PdfDocument.PageInfo =
            PdfDocument.PageInfo.Builder(PDF_PAGE_WIDTH, PDF_PAGE_HEIGHT, 1).create()
 
        return myPageInfo
    }
 
    // Draw Title
    fun titleText(text : String, canvas: Canvas) {
        title.setTypeface(fontStrawberry)
        title.color = ContextCompat.getColor(context, R.color.orange_80)
        title.textSize = 16f
        title.textAlign = Paint.Align.CENTER
 
        // Draw Text
        canvas.drawText(text, (canvas.width / 2).toFloat(), (canvas.height / 2).toFloat(), title)
 
    }
 
 
    @Throws(IOException::class)
    fun savePdfFileExternalStorage(filename: String, document: PdfDocument, context : Context) {
 
        try {
            // First, creating External Storage Directory, and a file name,
            // Second, write our PDF file to that location.
            // Third, close file
            val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
            val file = File(dir, filename)
            val fos = FileOutputStream(file)
            document.writeTo(fos)
            fos.close()
 
            // on below line we are displaying a toast message as PDF file generated..
            Toast.makeText(context, "PDF 파일이 다운로드 폴더에 저장되었습니다.", Toast.LENGTH_SHORT).show()
        } catch (e: Exception) {
            // below line is used
            // to handle error
            e.printStackTrace()
 
            // on below line we are displaying a toast message as fail to generate PDF
            Toast.makeText(context, "PDF파일 작성에 실패했습니다.", Toast.LENGTH_SHORT).show()
        }
    }
 
}

2. 유틸리티 클래스 사용하기

위에서 만들 클래스는 다음과 같이 사용할 수 있다.

pdfgenerator.kt
fun generatePDFStart(context: Context)
{
    // creating a PDF Document instance
    val pdfDocument: PdfDocument = PdfDocument()
 
    // PDFUtil instance
    val pdfUtil = PDFUtil(context)
    // Page1 Start
    val page1: PdfDocument.Page = pdfDocument.startPage(pdfUtil.A4)
 
    // creating a variable for canvas from our page of PDF.
    val canvas: Canvas = page1.canvas
 
 
    pdfUtil.titleText("이건 고령 딸기체 글자입니다.", canvas)
 
 
    // End of Page
    pdfDocument.finishPage(page1)
 
 
    // Save PDF file
    pdfUtil.savePdfFileExternalStorage("StartPDF.pdf", pdfDocument, context)
 
    // Closing PDF Document
    pdfDocument.close()
}

이렇게 하면 상당히 코드가 깔끔해진다.

다음번 부터는 이런 방식으로 소제목 만들기와 표 만들기를 해보자.