lifecarelog
프론트엔드

CJK 캡션 잘림 방지: 캔버스 자동 줄바꿈과 축소 구현기

캔버스에는 word-break가 없어요. measureText와 Intl.Segmenter로 CJK 캡션 자동 줄바꿈·폰트 축소를 구현한 과정을 정리했어요.

5분 읽기

앱스토어 스크린샷에 일본어 캡션을 얹어 보니 문장이 디바이스 프레임 밖으로 삐져나가 있었어요. 영어로는 멀쩡하던 레이아웃이었는데요. 혹시 캔버스에 다국어 텍스트를 그리다 비슷한 일을 겪은 적 없나요?

왜 공백 기준 줄바꿈이 안 통할까

영어는 공백 단위로 자르면 대충 맞아요. 문제는 일본어와 중국어엔 문장에 공백이 아예 없다는 점이에요. 한국어는 공백이 있긴 하지만 한 어절이 길어서 어절 단위로만 자르면 금방 넘쳐요.

DOM이라면 CSS word-break 한 줄로 끝날 일이에요. 그런데 스크린샷 렌더링에 쓰는 캔버스의 fillText에는 줄바꿈이라는 개념 자체가 없어요. 폭 계산부터 줄 나누기까지 전부 직접 해야 해요.

3단계로 잡은 줄바꿈 로직

LocaleShots는 디바이스 프레임 위에 캡션을 캔버스로 그리는 구조예요. 여기에 붙인 로직은 3단계예요.

Intl.Segmenter로 조각 분리measureText로 폭 재며 줄 나누기그래도 넘치면 폰트 축소

먼저 Intl.Segmenter의 word 단위로 문장을 조각내요. 직접 돌려 보니 공백 없는 일본어 문장도 꽤 자연스러운 경계를 찾아줘요. 브라우저 내장 API라 추가한 의존성은 0개고요.

다음은 조각을 한 줄에 하나씩 붙이면서 measureText로 폭을 재요. 캡션 영역 폭을 넘는 순간 줄을 나누는 방식이에요.

for (const seg of segments) {
  const next = line + seg;
  if (ctx.measureText(next).width > maxWidth) {
    lines.push(line);
    line = seg;
  } else {
    line = next;
  }
}

마지막으로 행두 금칙 처리를 넣었어요. 마침표나 닫는 괄호가 줄 머리에 오면 어색해서, 그 경우엔 앞 줄의 마지막 글자를 함께 끌어내려요.

그래도 넘치면 폰트를 줄여요

캡션 영역은 2줄 제한을 뒀어요. 2줄로도 안 들어가면 폰트 크기를 줄이는데, 1px씩 줄이며 재측정하면 레이아웃 계산이 수십 번 반복돼요. 그래서 상한 64px, 하한 28px 범위를 이진 탐색으로 좁혔어요. 계산상 6번 안에 맞는 크기가 확정돼요.

테스트해 보면 캡션 한 장에선 체감 차이가 없어요. 다만 언어 10개에 스크린샷 5장씩 일괄 렌더링하면 50장이라, 반복 계산은 줄일수록 좋더라고요.

하한 28px에 걸리면 더 줄이지 않고 경고를 띄우게 했어요. 읽을 수 없는 캡션은 잘린 캡션보다 나을 게 없어서요.

캔버스에 글자를 얹는 순간 생기는 일

플랜비(plan-b)의 전술보드를 만들 때도 느꼈지만, 캔버스에 텍스트를 올리는 순간 브라우저가 공짜로 해주던 일이 전부 제 일이 돼요. 줄바꿈, 말줄임, 넘침 처리까지요. 적용해 봤더니 로직 자체는 함수 하나로 끝나는 크기였어요. 무서워서 미루기엔 아까운 작업이에요.

화면에 보이는 글자의 책임이 누구에게 있는지 먼저 확인해 보세요. 캔버스라면, 그건 당신 몫이에요.

#Canvas#i18n#텍스트렌더링

라이프케어로그 서비스가 궁금하신가요?

AI 기반 건강·일정·재활 관리 앱을 직접 써보세요.

서비스 살펴보기

관련 글

댓글

아직 댓글이 없어요. 첫 댓글을 남겨주세요.