목차
목적
여러 줄(Multi line)
다음이 전체 코드이다.
// body Text fun bodyText(text : String, canvas: Canvas) { val textLayout = StaticLayout.Builder.obtain(text, 0, text.length, cellBodyLeft, 400) .setAlignment(Layout.Alignment.ALIGN_NORMAL) .setLineSpacing(0f, 1.2f) .setIncludePad(true).build() canvas.save() canvas.translate(50f, 350f) textLayout.draw(canvas) canvas.restore() }
1. static layout
가. obtain
obtatin 함수는 다음과 같이 인자를 받는다.
obtain(CharSequence source, int start, int end, TextPaint paint, int width)
첫번째는 텍스트, 두번째 시작하는 문자, 세번째는 끝나는 문자의 위치, 네번째는 페인터, 마지막은 레이아웃의 너비이다.
여기서 페인터는 통상읜 Paint가 아니라 TextPaint임을 유의하자. TextPaint는 Paint를 상속받으므로,
기존에 Paint로 선언했다면, 그냥 TextPaint로 선언하면 된다.
obtain함수에서 글자를 그릴 너비를 받아서, 이 너비 이상이 되면 줄 바뀜을 하게 해준다.
나. setAlignment
글자를 정렬하는 것이다. Layout.Alignment를 enum으로 변수롷 받는다.
디폴트값은 디폴트 값은 Layout.Alignment#ALIGN_NORMAL 이다1)).
각각 다음과 같다.
- ALIGN_NORMAL : 왼쪽 정렬
- ALIGN_CENTER : 가운데 정렬
- ALIGN_OPPOSITE : 오른쪽 정렬
위 그림은 다음의 코드를 이용하여 구현하였다.
(1) 구현 함수
fun bodyText(text : String, canvas: Canvas, layout : Layout.Alignment) { val textLayout = StaticLayout.Builder.obtain(text, 0, text.length, cellBodyLeft, 400) .setAlignment(layout) .setLineSpacing(0f, 1.2f) .setIncludePad(true).build() canvas.save() canvas.translate(50f, currentPOS.Y.toFloat()) textLayout.draw(canvas) canvas.restore() currentPOS.Y+= textLayout.height + 20 }
(2) 호출함수
pdfUtil.headerText("1. ALIGN_NORMAL", canvas) pdfUtil.bodyText("우리는 민족중흥의 역사적 사명을 띠고 이 땅에 태어났다. 조상의 빛난 얼을 오늘에 되살려, 안으로 자주독립의 자세를 확립하고, 밖으로 인류 공영에 이바지할 때다. 이에, 우리의 나아갈 바를 밝혀 교육의 지표로 삼는다.\n" ,canvas) pdfUtil.headerText("2. ALIGN_CENTER", canvas) pdfUtil.bodyText("우리는 민족중흥의 역사적 사명을 띠고 이 땅에 태어났다. 조상의 빛난 얼을 오늘에 되살려, " + "안으로 자주독립의 자세를 확립하고, 밖으로 인류 공영에 이바지할 때다. 이에, 우리의 나아갈 바를 밝혀 교육의 지표로 삼는다.\n" ,canvas, Layout.Alignment.ALIGN_CENTER) pdfUtil.headerText("3. ALIGN_OPPOSITE", canvas) pdfUtil.bodyText("우리는 민족중흥의 역사적 사명을 띠고 이 땅에 태어났다. 조상의 빛난 얼을 오늘에 되살려, " + "안으로 자주독립의 자세를 확립하고, 밖으로 인류 공영에 이바지할 때다. 이에, 우리의 나아갈 바를 밝혀 교육의 지표로 삼는다.\n" ,canvas, Layout.Alignment.ALIGN_OPPOSITE)
다. setLineSpacing
public StaticLayout.Builder setLineSpacing (float spacingAdd, float spacingMult)
첫번째 인자는 추가할 라인의 갯수이고, 두번쨰 인자는 배율이다. 통상 (0, 1.2f)로 하면 보기에 괜찮다.
라. setIncludePad
폰트 메트릭스에서 ascent와 descent사이까지의 높이 외에 추가로 너비를 둘 것이냐이다. 디폴트 값은 true이다. 아랍어 때문에 이러한 개념이 필요하다는데2)), 그렇다면 한글에서는 굳이 이러한 개념이 필요가 없어 보이기도 한다.
2. canvas
위와 같은 레이아웃(layout)을 이용하려면 우선 기존 canvas를 save해야 한다.
그 다음에 layout를 위치할 자리를 cavas.translate로 정한다.
그 다음에 canvas를 restore하면, 레이아웃으로 만든 문장을 canvas에 그리게 된다.
canvas를 restore한 다음에는 다시 기존 canvas의 좌표계로 돌아올 것이다.
그런데 layout의 경우에는 height와 width를 알아낼 수 있으므로,
layout으로 출력한 여러 줄의 문장의 아래 위치는 다시 기존 canvas의 절대좌표로 지정할 수 있다.
이런 식으로 지정이 가능하다.
currentPOS.Y+= textLayout.height + 20
회전
위의 canvas는 회전이 가능하다. 따라서 layout이 적용되는 canvas를 회전시킨 후에, 다시 canvas를 restore하면 회전된 캔버스를 원본 캔버스 위에 겹치는게 가능하다.
이런식으로 말이다.
세로로 쓰기
1. 개요
글자를 세로로 쓰는 방법은 (1) 한 글자씩 루프를 돌린 후에 각 글자의 y축을 바꿔주는 방법 (2) 위의 staticLayout의 폭을 한 글자에 맞추는 방법을 생각할 수 있다.
staticLayout 개념을 연습하는김에 세로로 쓰기를 해보자
2. 글자의 폭 계산
글자는 각기 폭이 다르다.물론 한글의 경우 거의 대부분의 폭이 같을 것이나 반드시 장담할 수는 없다. 따라서 어떠한 문장 중에서 글자들은 제각기 폭이 다를 것이다. 따라서 안드로이드 API는 getTextWidths()라는 함수를 제공한다. 이를 통해 가장 긴 폭을 가진 글자의 폭을 반환받을 수 있다.
// 문장 중에서 가장 너비가 큰 글자의 너비 구하기 private fun getCharWidth(text : String, textPaint: TextPaint) : Float { var maxWidth = 0f val w = FloatArray(text.length) cellBodyLeft.getTextWidths(text, w) w.forEach { if (maxWidth < it) { maxWidth = it } } return maxWidth }
3. 텍스트 레이아웃의 폭을 지정하기
다음과 같이 가장 긴 글자의 폭을 staticLayout의 폭으로 지정하면, 별도로 y축의 포지션을 정해주지 않는다고 하더라도 세로쓰기가 가능할 것이다.
fun verticalTxt(text : String, canvas: Canvas) { val cWidth = getCharWidth(text, cellBodyLeft).toInt() val textLayout = StaticLayout.Builder.obtain(text, 0, text.length, cellBodyLeft, cWidth) .setLineSpacing(0f, 1.2f).build() canvas.save() canvas.translate(100f, currentPOS.Y.toFloat()) textLayout.draw(canvas) canvas.restore() currentPOS.Y+= textLayout.height + 20 }
로그인