소개
표 그리기
1. 개념
표를 그릴려면 어떻게 해야 할까? 여러가지 방법이 있을 것인데 하나는 큰 박스를 넣은 후 그 안에 선을 그리는 방법이 있을 것이다. 또다른 방법은 박스를 여러개 그리는 것이다. 이를테면 한 줄에 2개의 칸이 있는 2×1 표를 그린다고 해보자. 그러면 긴 박스와 작은 박스를 겹쳐서 그리면 될 것이다. 나는 선을 그리는 방법보다는 박스를 겹치는 방법을 선택했다.
왜냐하면 위의 소개 그림에서도 보듯이 셀에 음영을 넣는 경우가 생길텐데, 음영을 넣으려면 이 역시 박스를 그리는 방식으로 진행된다. 따라서 선을 그리고 음영을 넣는다는 것은 중복된 일을 하는 것가 마차가지가 되는 것이다. 그리고 박스를 중첩하는 방식으로 표를 만들면, 각 셀 안에 들어간 텍스트의 위치를 계산하는 것도 쉽다.
2. 셀 그리기
표를 그리려면 기준점이 있어야 한다. 좌측 상단을 기준점으로 잡는 것이 통상적이고, 가장 이해하기 쉬울 것이다. 좌측 상단을 기준으로 2개의 박스를 겹쳐서 1줄짜리 표를 그리면 다음과 같이 될 것이다.
따라서 다음의 코드가 될 것이다.
/** 변수 **/ 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)
참고로 세로좌표의 경우에는 오프셋을 추가하였는데, 이는 글자의 베이스라인은 좌측하단에 있기 때문이다.
안드로이드의 폰트메트릭스에 대해서는 이전 포스팅을 참고하라.
결론
이 포스팅의 서두부분에 나온 표를 그린다면 다음의 코드를 사용하면 될 것이다.
- tablePlaintiff.kt
// 표 : 고소인 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() } }
로그인