===== 소개 =====
다음과 같이 표를 그리고, 데이터베이스에서 가져온 값을 표에 넣을 것이다.
{{:android:pdfdocument:tabhledrawing.jpg?400|tabledrawing}}
===== 표 그리기 =====
==== 1. 개념 ====
표를 그릴려면 어떻게 해야 할까? 여러가지 방법이 있을 것인데 하나는 큰 박스를 넣은 후 그 안에 선을 그리는 방법이 있을 것이다.
또다른 방법은 박스를 여러개 그리는 것이다. 이를테면 한 줄에 2개의 칸이 있는 2X1 표를 그린다고 해보자. 그러면 긴 박스와 작은 박스를 겹쳐서 그리면 될 것이다. 나는 선을 그리는 방법보다는 박스를 겹치는 방법을 선택했다.
왜냐하면 위의 소개 그림에서도 보듯이 셀에 음영을 넣는 경우가 생길텐데, 음영을 넣으려면 이 역시 박스를 그리는 방식으로 진행된다. 따라서 선을 그리고 음영을 넣는다는 것은 중복된 일을 하는 것가 마차가지가 되는 것이다. 그리고 박스를 중첩하는 방식으로 표를 만들면, 각 셀 안에 들어간 텍스트의 위치를 계산하는 것도 쉽다.
==== 2. 셀 그리기 ====
표를 그리려면 기준점이 있어야 한다. 좌측 상단을 기준점으로 잡는 것이 통상적이고, 가장 이해하기 쉬울 것이다. 좌측 상단을 기준으로 2개의 박스를 겹쳐서 1줄짜리 표를 그리면 다음과 같이 될 것이다.
{{:android:pdfdocument:tabledrawing.png?600|table drawing}}
따라서 다음의 코드가 될 것이다.
/** 변수 **/
val cellHeight = 30 // Row Height : 40
val columnWidth = 180f // 내부선
val vcOffset = 4f // 세로 중간을 맞추기 위한 오프셋
// Rect
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat(), endOfBody, currentPOS.Y.toFloat() + cellHeight, linePaint) // 첫쨰 줄
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat(), currentPOS.X.toFloat() + columnWidth, currentPOS.Y.toFloat() + cellHeight, linePaint) // 제목박스
여기서 endOfBody는 본문 영역의 너비를 말한다. 사전에 다음과 같이 정의되었다.
// 여백
val marginTop = 42
val marginBottom = 50
val marginLeft = 47
val marginRight = 48
// end of 본문
val endOfBody = (PDF_PAGE_WIDTH - marginRight).toFloat()
이러한 셀들을 여러개를 연결하면 표가 될 것이다.
==== 3. 셀안에 글자를 집어 넣기 ====
각 셀의 좌측상단을 기준점으로 잡았으므로 글자는 이를 기준으로 상대 좌표를 구하면 된다.
만약 셀안에 가로정렬을 한다고 해보자. 셀 안의 가운데 점을 잡아야 하므로
가로좌표는 기준점 + (cellwidth) / 2가 될 것이다. 세로좌표는 기준점 + (cellHeight) / 2가 될 것이다.
따라서 셀 안에 글자를 집어 넣는 것은 다음 코드가 될 것이다.
// Text
canvas.drawText(header, currentPOS.X.toFloat() + columnWidth / 2, currentPOS.Y.toFloat() + cellHeight / 2 + vcOffset, cellHeaderLight)
canvas.drawText(body, currentPOS.X.toFloat() + columnWidth + (endOfBody - (currentPOS.X + columnWidth)) / 2, currentPOS.Y.toFloat() + cellHeight / 2 + vcOffset, cellBody)
참고로 세로좌표의 경우에는 오프셋을 추가하였는데, 이는 [[android:pdfdocument:measuringparagraph#글자의 경계선 확인하기|글자의 베이스라인]]은 좌측하단에 있기 때문이다.
안드로이드의 폰트메트릭스에 대해서는 [[android:pdfdocument:measuringparagraph|이전 포스팅]]을 참고하라.
===== 결론 =====
이 포스팅의 서두부분에 나온 표를 그린다면 다음의 코드를 사용하면 될 것이다.
// 표 : 고소인
fun tablePlaintiff(canvas: Canvas) {
/** 변수 **/
val cellHeight = 30 // Row Height : 40
val columnWidth = 60f // 내부선
val secondColumn = 260f // 두번쨰 열
val vcOffset = 4f // 세로 중간을 맞추기 위한 오프셋
plaintiffList?.forEach() {
// 첫째 행
// Rect
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat(), endOfBody, currentPOS.Y.toFloat() + cellHeight, linePaint) // 첫쨰 줄
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat(), currentPOS.X.toFloat() + columnWidth, currentPOS.Y.toFloat() + cellHeight, cellFill) // 제목박스
canvas.drawRect(currentPOS.X.toFloat() + secondColumn, currentPOS.Y.toFloat(), currentPOS.X.toFloat() + secondColumn + columnWidth, currentPOS.Y.toFloat() + cellHeight, cellFill) // 두번째 제목박스
// Text
canvas.drawText("이 름", currentPOS.X.toFloat() + columnWidth / 2, currentPOS.Y.toFloat() + cellHeight / 2 + vcOffset , cellHeader)
canvas.drawText(it.pName, currentPOS.X.toFloat() + columnWidth + (secondColumn - columnWidth) / 2, currentPOS.Y.toFloat() + cellHeight / 2 + vcOffset, cellBody)
canvas.drawText("주민번호", currentPOS.X.toFloat() + secondColumn + columnWidth / 2, currentPOS.Y.toFloat() + cellHeight / 2 + vcOffset, cellHeader) // 2번쨰 칸
canvas.drawText(it.socialNumber, currentPOS.X.toFloat() + secondColumn + columnWidth + (endOfBody - (currentPOS.X + secondColumn + columnWidth)) / 2, currentPOS.Y.toFloat() + cellHeight / 2 + vcOffset, cellBody)
// 둘재 행
// Rect
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat() + cellHeight, endOfBody, currentPOS.Y.toFloat() + cellHeight * 2, linePaint) // 둘쨰 줄
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat() + cellHeight, currentPOS.X.toFloat() + columnWidth, currentPOS.Y.toFloat() + cellHeight * 2, cellFill) // 제목박스
// Text
canvas.drawText("주 소", currentPOS.X.toFloat() + columnWidth / 2, currentPOS.Y.toFloat() + cellHeight * 1 + cellHeight / 2 + vcOffset, cellHeader)
canvas.drawText(it.pAddress, currentPOS.X.toFloat() + columnWidth + (endOfBody - (currentPOS.X + columnWidth)) / 2, currentPOS.Y.toFloat() + cellHeight * 1 + cellHeight / 2 + vcOffset, cellBody)
// 셋쨰 행
// Rect
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat() + cellHeight * 2, endOfBody, currentPOS.Y.toFloat() + cellHeight * 3, linePaint) // 셋쨰 줄
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat() + cellHeight * 2, currentPOS.X.toFloat() + columnWidth, currentPOS.Y.toFloat() + cellHeight * 3, cellFill) // 제목박스
// Text
canvas.drawText("연 락 처", currentPOS.X.toFloat() + columnWidth / 2, currentPOS.Y.toFloat() + cellHeight * 2 + cellHeight / 2 + vcOffset, cellHeader) // 제목
canvas.drawText(it.pPhoneNumber, currentPOS.X.toFloat() + columnWidth + (endOfBody - (currentPOS.X + columnWidth)) / 2, currentPOS.Y.toFloat() + cellHeight * 2 + cellHeight / 2 + vcOffset, cellBody) // 내용
// 아래 여백
totalPOS += POS(0, cellHeight * 3 + 20)
currentPOS += POS(0, cellHeight * 3 + 20)
}?:{
canvas.drawRect(currentPOS.X.toFloat(), currentPOS.Y.toFloat(), endOfBody, currentPOS.Y.toFloat() + cellHeight, linePaint) // 첫쨰 줄
canvas.drawText("당사자 정보가 없습니다. 당사자를 추가했는지 확인하세요!", currentPOS.X.toFloat() + endOfBody / 2, currentPOS.Y.toFloat() + cellHeight / 2 + vcOffset , cellHeader)
// 아래 여백
linefeed()
}
}