사용자 도구

사이트 도구


android:pdfdocument:commandmirroring
commandmirroring

문서의 이전 판입니다!


목적

저번 포스트에서는 각 구역을 나누어 머릿말(Header)과 꼬리말(Footer)를 만들어 보았는데, 전체 페이지수를 알려면 일단 모두 그려보아야만 알 수 있으므로 불가피하게 본문 부분을 비트맵으로 저장하여 PDF Canvas에 그려주는 방식을 택하였다.

그런데 이렇게 하면 본문 부분은 비트맵에 불과하므로 확대를 하면 글자가 깨지는 문제점이 생긴다.

따라서 다음과 같이 본문 부분 역시 벡터로 그리려고 한다.

본문 역시 벡터로 그려진 PDF
Vector1 Vector2

명령어 모음 만들기

1. 원리

지금까지 PDF로 그린 것을 명령어로 저장하여 다시 반복하려는 이유는 페이지가 여러장일 때에는 구역화(헤더와 푸터, 본문)를 할 때에전체 장수를 미리 알 수가 없기 때문이다. 즉, 한번 싹 다 그려야만 전체의 페이지수를 알 수 있기 때문에, 다시 반복하는 것이다.

그런데 지금까지 PDF를 그릴 떄에는, 정해진 사이즈의 캔버스 위에 특정한 좌표에다가 드로잉을 했다. 그리고 그 드로잉에는 인쇄될 글자들이 포함된다. 따라서 좌표와 명령어, 그리고 문구를 변수로 넘겨주면 명령어의 모음이 된다.

2. 구조

따라서 명령어의 구조를 다음과 같이 만들었다.

// 명령어 구조
data class COMMAND(
    val command : String,
    val page : Int,
    val textFirst : String,
    val textSecond : String,
    val pos : POS
)

3. 명령어 집합 선언

다음과 같이 명령어 집합을 선언했다.

    // 명령어 집합 
    val commandList : ArrayList<COMMAND> = arrayListOf()
    var isRecording = true

isRecording을 통하여 명령어를 기록하는 스위치를 만들었다. 이에 대하여는 다음 항목에서 서술하겠다.

4. isRecording을 통한 제어

가. 제어 함수 만들기

isRecording이라는 스위치를 통하여, 명령어를 저장할 때에 현재 상태의 변화(예 : 좌표의 변화)를 적용할지 여부를 고를 수 있게 했다.

isRecording은 다음의 함수로 호출할 것이다.

    // Recording commannd
    private fun recording(recoding : Boolean) {
        isRecording = recoding
    }

나. 내가 했던 일을 기록

다음과 같이 기존 그리기 함수에다가 내가 하는 그리기를 명령어 셋트로 기록하게 했다.

isRecording이 필요한 이유는, 아래에서와 같이 기록할떄의 좌표 다음에도 좌표를 옮길 필요가 있을 수 있기 때문이다.

// Draw Title Text
fun titleText(text : String) {
 
    nextPage()
    linePlus(50)
    canvasList[pageNum].drawText(text, (canvasList[pageNum].width / 2).toFloat(), currentPOS.Y.toFloat(), title)
 
    if (isRecording) {
        commandList.add(COMMAND("TitleText", pageNum, text, "", currentPOS))
    }
 
    linePlus(50)
}

위의 코드에서는 제목그리기(titleText)를 실행한 후,. 이를 기록한 이후에도 다시 좌표를 세로로 50포인트 내리게 했다.

그리기 이후의 좌표 설정은 굳이 명령어를 기록할 떄 필요 없는 일이다.

커맨드는 첫번째 인자로 사용할 컴맨드(=TitleText), 두번쨰 인자로 현재의 페이지수, 세번쨰 인자로 넘겨줄 텍스트, 네번째 인자로는 두번째 텍스트이다(그런데 제목만 그려주는 이 경우에는 두번째 텍스트는 없다). 그리고 마지막으로 다섯번째 인자는 현재 페이지 내에서의 좌표이다.

5. 두번째 PDF 문서를 그리기

가. 새로운 그리기 함수

첫번째 그리기와 동일한 기능을 하는 두번째의 그리기 함수가 필요하다.

첫번째 그릴 때에는 그리면서 페이지수를 알게 된 것이지만, 두번쨰 pdf 페이지를 그릴 때에는 현재 페이지수를 알고 있으며, 이에 걸맞는 canvas가 주어지게 된다. 따라서 첫번쨰 그릴 떄에는 canvas의 배열(canvasList)을 이용하였지만 두번째에서는 canvas를 지정하여 인자로 넘겨줄 수 있다.

// Draw Title Finally
fun titleText(text : String, canvas: Canvas, pos : POS) {
 
    linePlus(50)
    canvas.drawText(text, (body.Width / 2).toFloat(), pos.Y.toFloat(), title)
}

사실 두번째에 그릴 떄에는 첫번쨰 그릴때에 확정한 위치(pos)를 가져와서 canvas에 그리는 것이므로,. 그리고 난 이후에는 위치를 조정할 필요가 없다.

나. 전체 페이지 중 본문을 그리기

이전에 섹셔닝에서는 본문부분을 비트맵그림으로 그렸었다. 이를를 다음과 같이 2번째 그리기로 바꾸었다.

// PDF 페이지 만들기
fun makePDFDocument() {
 
    recording(false)
    for (i in 0 until canvasList.size) {
        val newPage: PdfDocument.Page = pdfDocument.startPage(A4)
 
        val canvas = newPage.canvas
 
        drawPageHeader(canvas)
//            canvas.drawBitmap(bitmapList[i], body.rectP.left.toFloat(), body.rectP.top.toFloat(), null)
        canvas.save()
        canvas.translate(marginLeft.toFloat(), marginTop.toFloat())
        todoList(commandList, canvas, i)
        canvas.restore()
        drawPageFooter(canvas, i + 1)
        pdfDocument.finishPage(newPage)
 
    }
}

todoList에는 cavnas의 페이지 순서를 인자로 내보내 준다.

다. 기록한 명령어를 호출하기

위에서 본문 부분에 그리기로 한 todoList는 다음과 같이 만들었다.

// Command Function
fun todoList(commands : ArrayList<COMMAND>, canvas: Canvas, page : Int) {
    val todo = commands.filter { it.page == page }
    todo.forEach() {
        when (it.command) {
            "TitleText" -> titleText(it.textFirst, canvas, it.pos )
            "HeaderText"->  headerText(it.textFirst, canvas, it.pos)
            "BodyText" -> bodyText(it.textFirst, canvas, it.pos)
            "BodyTextAlign" -> {
                when (it.textSecond) {
                    "NORMAL" -> bodyText(it.textFirst, Layout.Alignment.ALIGN_NORMAL, canvas, it.pos)
                    "CENTER" -> bodyText(it.textFirst, Layout.Alignment.ALIGN_CENTER, canvas, it.pos)
                    "OPPOSITE" -> bodyText(it.textFirst, Layout.Alignment.ALIGN_OPPOSITE, canvas, it.pos)
                }
            }
            "BodyTextDegree" -> bodyText(it.textFirst, it.textSecond.toFloat(), canvas, it.pos)
            "VerticalText" -> verticalTxt(it.textFirst, canvas, it.pos)
            "TablePlaintiff" -> tablePlaintiff(canvas, it.pos, it.textSecond)
            "NoneContact" -> noneContact(canvas, it.pos)
        }
 
    }
}

todoList는 제일 처음에 각 페이지별로 filter를 한다. 만약 페이지별로 커맨드를 나누지 않으면, 동일한 페이지, 동일한 좌표에 여러개의 커맨드가 중첩적용될 수 있기 때문이다. 즉, 페이지를 구분하여 특정의 canvas에 적용하는 캔버스가 무엇인지를 명확히 했다.

그 이후에는 COMMAND배열의 첫번쨰 인자인 command항목에 따라서 특정의 명령어를 실행하도록 하였다.

이런식으로 저장한 명령어 모음을 다시 실행하도록 하였다.

고소인과 피고소인 테이블 만들기

1. 목적

고소인과 피고소인은 한번에 그릴 때에 여러개의 변수가 같이 넘어간다.

이를테면, 고소인1명에 대한 표를 그릴 때에는 성명, 주소, 연락처, 주민등록번호와 같은 데이터들을 한번에 그려야 하는 것이다.

따라서 이들을 JSON으로 변환하여 위의 COMMAND클래스에 2번쨰 스트링인자에 변수로 넘겨주어야 한다.

2. JSON 만들기와 가져오기

가. JSON 만들기

다음과 같이 고소인 연락처를 JSON 스트링으로 만드는 함수를 만들었다.

//  고소인, 피고소인 연락처 JSON으로 만들기 
fun setJsonContactPlaintiff(contact : PlaintiffContact) : JSONObject {
 
    val jsonContact = JSONObject()
    try {
 
        jsonContact.put("name", contact.pName)   // 이름
        jsonContact.put("socialNumber", contact.socialNumber)  // 주민번호
        jsonContact.put("address", contact.pAddress) // 주소
        jsonContact.put("phoneNumber", contact.pPhoneNumber)  //  연락처
 
    } catch (e: Exception) {
        // TODO Auto-generated catch block
        e.printStackTrace()
    }
 
    return jsonContact
}
로그인하면 댓글을 남길 수 있습니다.

android/pdfdocument/commandmirroring.1734798708.txt.gz · 마지막으로 수정됨: 2024/12/22 01:31 저자 이거니맨