- 아랍어 웹 타이포그래피는 글자 연결, 양방향 텍스트, 숫자·구두점 처리, 줄 맞춤이 함께 얽힌 렌더링 인프라 문제이며 단순 CSS 버그로 처리하기 어려움
- 고전 아랍어 조판은 단어 사이 공백이 아니라 글자 내부 획을 늘리는 카시다(kashida) 로 양쪽 정렬을 구현했지만, 현대 브라우저의
text-align: justify는 주로 단어 사이 공백을 늘림 - 아랍어 글자는 저장된 코드포인트 하나가 문맥에 따라 고립형·초성형·중성형·종성형으로 바뀌며, OpenType 기능과 셰이핑 엔진 없이는 글자가 분리된 형태로 렌더링됨
- Unicode의 Arabic Presentation Forms, 숫자 체계, UAX #9 양방향 알고리듬, 보이지 않는 제어문자는 검색 실패·전화번호 역전·커서 이동 혼란 같은 실제 제품 문제로 이어짐
- HarfBuzz, Amiri, W3C Arabic Layout Requirements 등 핵심 기반은 구축됐지만, 브라우저의 아랍어 정렬과
jstf활용은 여전히 구현 공백으로 남아 있음
시작점: “CSS 버그”처럼 보인 아랍어 조판 문제
- 고객용 대시보드의 혼합 콘텐츠 아랍어 문단이 디자인 시안처럼 양쪽 정렬되지 않고 왼쪽 가장자리가 들쭉날쭉하게 렌더링됨
- 같은 블록의 라틴 문자 버전은 “fine”으로 보였지만, 아랍어에서는 줄이 오른쪽에서 시작하므로 들쭉날쭉한 가장자리가 왼쪽에 생김
text-align: justify를 적용해도 디자인팀이 승인한 형태처럼 단어 내부 획을 늘려 줄을 채우지 못함- 같은 제품에서 이전에도 PDF의 이름 글자 분리, 검색 인덱스 실패, 레거시 Unicode 코드포인트 문제 등 아랍어 처리 문제가 반복됨
- 문제의 본질은 특정 스타일시트의 결함이 아니라 웹의 아랍어 타이포그래피 상태에 있음
필사 전통이 해결했던 문제
- 고전 아랍어 필사 전통은 단어 사이 공백을 늘리지 않고 글자 형태 내부의 연결 획을 늘려 줄을 양쪽 정렬함
- 이 방식은 taṭwīl 또는 현대 기술 용어로 kashida라고 불리며, 특정 글자 쌍 사이 연결 획을 길게 늘리는 방식임
- 17세기 Naskh의 잘 짜인 페이지는 양쪽 여백이 맞고 단어 간격이 늘어나지 않아 조밀하고 규칙적인 질감을 만듦
- Ibn Muqla가 정리한 al-khaṭṭ al-mansūb는 갈대 펜촉의 마름모 점, alif 높이, 원호 비율 등으로 글자 형태를 체계화함
- 이 전통에서 줄 맞춤은 공백 배분 문제가 아니라 글자 형태와 대체 글리프를 고르는 셰이핑 문제였음
글자 하나, 네 가지 형태
- 아랍어는 항상 필기체처럼 이어지는 문자이며, 인쇄체와 손글씨를 나누는 블록 글자 구분이 없음
- 각 글자는 이웃 글자에 따라 고립형, 초성형, 중성형, 종성형으로 달라지고, 여섯 글자는 앞쪽으로 연결되지 않아 단어 내부 흐름을 끊음
- Unicode는 추상 글자를 저장하고, 글꼴은 위치별 글리프를 제공하며, 셰이핑 엔진은
isol,init,medi,fina,rlig,mark,mkmk같은 OpenType 기능을 적용함 محمد같은 단어는 저장상 네 코드포인트지만, 렌더링 시 여러 글리프 선택과 OpenType 조회를 거쳐 하나의 이어진 획으로 보임- HarfBuzz 같은 셰이핑 엔진이 없거나 PDF 생성기가 이를 거치지 않으면 같은 코드포인트가 서로 분리된 고립형 글자로 렌더링됨
Unicode의 화석: Arabic Presentation Forms
- DOS와 초기 Windows 시대의 8비트 코드페이지는 추상 글자가 아니라 초성형·중성형 같은 형태 자체를 별도 문자로 인코딩함
- Unicode는 왕복 호환성을 위해 이를 받아들였고, U+FB50부터 U+FEFF까지 Arabic Presentation Forms 블록으로 남아 있음
- 새 문서에는 이 코드포인트가 들어가면 안 되지만, PDF 텍스트 추출기는 오늘날에도 이를 내보낼 수 있음
- 같은 이름이 현대 Unicode와 Presentation Forms로 각각 저장되면 화면에서는 동일하게 보이지만 문자열 비교와 검색에서는 서로 다르게 처리됨
- NFKC 정규화를 적용하면 Presentation Forms를 추상 글자로 접어 검색 누락을 줄일 수 있음
셰이핑과 양방향 처리를 건너뛴 소프트웨어
- 셰이핑 엔진과 양방향 알고리듬을 건너뛰는 소프트웨어는 글자를 하나씩 고립형으로 그리고 줄을 왼쪽에서 오른쪽으로 배치함
- 이런 출력은 상점 간판, 탑승권, 워터마크, 오래된 영화 관련 아랍어 표기 등에서 아랍어 독자가 실제로 마주치는 형태임
- 오래된 Photoshop, 기본 설정의 matplotlib, npm의 여러 PDF 생성기, 영수증 프린터가 이런 문제를 낼 수 있음
- Python의 흔한 우회책인
arabic_reshaper와python-bidi는 Presentation Forms 블록을 사용해 미리 셰이핑된 형태를 문자열에 구워 넣음 - 이 우회책은 렌더러가 해야 할 일을 문자열에 앞당겨 넣는 방식이며, 근본적으로 텍스트 스택의 결함을 드러냄
세 종류의 숫자와 구두점 문제
- 세계가 “Arabic numerals”라고 부르는 0–9는 대부분의 아랍어 독자가 일상적으로 쓰는 숫자 모양이 아님
- 이집트, 수단, 레반트, 이라크, 걸프 지역은 Unicode의 ARABIC-INDIC DIGITS
٠١٢٣٤٥٦٧٨٩를 사용함 - 마그레브 지역은 라틴 숫자 글리프를 사용하고, 이란·아프가니스탄·파키스탄은 EXTENDED ARABIC-INDIC DIGITS
۰۱۲۳۴۵۶۷۸۹를 사용함 - 숫자는 UAX #9에서 강한 문자가 아니라 약한 문자로 처리되며, 앞선 강한 문자의 방향성에 따라 유럽 숫자 또는 아랍 숫자로 재분류됨
- 아랍어 단어 뒤의
010-1234-5678같은 전화번호는 하이픈이 중립 처리되면서 화면에서5678-1234-010처럼 순서가 바뀔 수 있음 - 플랫폼이 제공하는 해결책은 번호를
‎또는<bdi>로 감싸 방향성을 분리하는 방식임 - 아랍어권의 소수점과 천 단위 구분자는 U+066B
٫와 U+066C٬이며, ASCII.와,는 거의 비슷해 보이지만 코드포인트와 양방향 속성이 다름
인쇄부터 웹까지 이어진 우회와 단순화
- 1514년 Fano에서 인쇄된 _Kitāb Ṣalāt al-Sawāʿī_는 최초의 이동식 아랍어 활자본으로, 글자 연결 분리와 점 위치 오류가 보이는 사례임
- 1537년 Venice의 Paganini Qurʾān은 조판 오류와 텍스트 오류가 겹쳐 상업적으로 실패했고, 한 부가 1987년 Venice의 수도원 도서관에서 발견됨
- Ottoman 인쇄 금지 이야기는 Bayezid II와 Selim I의 칙령 원문이 남아 있지 않으며, 유럽 여행자 기록에 의존한다는 문제가 있음
- Cairo의 Bulaq Press는 1820년 Muhammad Ali가 세웠고, 수백 개 활자 조각과 많은 인내를 통해 아랍어 금속활자의 품질을 끌어올림
- 1924년 Cairo Qurʾān은 Amiria Press에서 금속활자로 제작됐고, 20세기 텍스트와 타이포그래피 표준화에 기여함
- 1950년대 후반 Kamel Mrowa와 Linotype은 90채널 매거진에 맞추기 위해 초성형을 중성형에, 종성형을 고립형에 합치고 합자를 줄인 Simplified Arabic을 만듦
- Simplified Arabic은 저렴하고 빠른 신문 제작을 가능하게 했고, 한 세대 안에 아랍어 뉴스룸을 장악함
웹이 아직 그리지 못하는 카시다
- CSS Text Module Level 3의 초기 초안에는
text-justify값으로 kashida가 있었고, Internet Explorer 5.5는 2000년에 이를 구현함 - IE 5.5는
text-kashida-space속성도 제공했지만, 다른 브라우저가 구현하지 않으면서 해당 값은 사양에서 빠짐 - 현대 Chrome, Firefox, Safari의
text-align: justify는 아랍어에서 단어 사이 공백을 늘리는 방식으로 동작함 - CSS Working Group의 아랍어 정렬 이슈는 적어도 2015년부터 열려 있고, W3C Arabic Layout Requirements 작업도 같은 해 시작됨
- 카시다 정렬은 늘어난 글리프가 폭을 바꾸고, 폭 변화가 줄바꿈과 필요한 늘림 폭을 다시 바꾸기 때문에 셰이핑과 레이아웃이 줄 단위로 반복 협상해야 함
- OpenType에는 1990년대부터 글꼴이 정렬 우선순위를 알릴 수 있는
jstf테이블이 있었지만, 셰이핑 엔진은 거의 읽지 않고 글꼴 제작사는 거의 제공하지 않음 - Microsoft Word와 InDesign Middle East Edition은 카시다 정렬을 제공하지만, 사람들이 가장 많이 읽는 브라우저 렌더러 집합은 글자를 늘리지 못함
Tatweel 해킹과 합자·모음표 문제
- 웹에서 흔한 우회책은 텍스트 자체에 U+0640 TATWEEL 문자를 삽입해 늘어난 획처럼 보이게 만드는 방식임
- Tatweel은 콘텐츠를 바꾸므로 검색 매칭, 복사·붙여넣기, 화면낭독기, 컬럼 리플로우에서 문제가 생김
- Tatweel이 그리는 획은 글꼴과 문자의 규칙에 따라 배치되는 카시다가 아니라 작성자가 추측해 넣은 타자기식 막대임
- OpenType 합자는
rlig,liga,dlig로 나뉘며,lām-alif같은 필수 합자가 깨지면 문자는 단순히 보기 나쁜 수준을 넘어 잘못됨 - U+200C ZERO WIDTH NON-JOINER를 글자 사이에 넣으면 저장 글자는 유지되지만 렌더링은 각 글자를 고립형으로 강제함
- Safari는
"rlig" 0,"liga" 0을 무시하므로 필수 합자 비활성화 데모가 영향을 주지 않음 - Amiri는 Khaled Hosny가 2011년 SIL Open Font License로 공개한 Naskh 글꼴이며, 2022년 1.0 재작성 뒤 곡선형 카시다와 정교한 모음표 쌓기를 제공함
line-height: 1과overflow: hidden이 결합된 카드 컴포넌트는 완전 모음 표기된 아랍어의 위쪽 모음표를 잘라낼 수 있음
양방향 알고리듬과 거짓말하는 커서
- 아랍어 문단 안의 버전 번호, 영어 식별자, URL, 프랑스어 단어 등은 Unicode Bidirectional Algorithm인 UAX #9를 호출함
- 아랍어 글자는 강한 오른쪽-왼쪽 문자, 라틴 글자는 강한 왼쪽-오른쪽 문자, 숫자는 문맥을 따르는 약한 문자, 공백과 구두점은 중립 문자로 처리됨
- 알고리듬은 문자별 방향 클래스를 부여하고, 약한 문자와 중립 문자를 단계적으로 해석한 뒤, 임베딩 레벨을 배정하고 같은 레벨의 런을 뒤집음
- 화면의 시각 순서와 메모리의 논리 순서가 달라지므로 커서 이동, 마우스 클릭, 선택 동작은 두 순서 사이를 계속 번역해야 함
- 런 경계에는 논리 위치와 시각 위치라는 두 가지 합법적인 커서 위치가 있으며, Chrome, Firefox, Qt, Outlook은 이를 서로 다르게 처리할 수 있음
- 혼합 아랍어-영어 텍스트 작성은 2026년에도 주요 편집기, 이메일 클라이언트, 채팅 애플리케이션에서 기본적으로 인지 비용이 큰 경험으로 남아 있음
الصفحات 10-20같은 범위는 규칙 W2와 중립 하이픈 처리 때문에 “20에서 10까지”처럼 보일 수 있으며, U+200E LEFT-TO-RIGHT MARK를 앞에 넣어 고칠 수 있음
작동하는 기반과 남은 공백
- Khaled Hosny는 Amiri를 만들었고, HarfBuzz 명령줄 도구
hb-shape를 작성했으며, HarfBuzz 공동 유지보수자 역할도 함 - Behdad Esfahbod는 Hosny 이전에 HarfBuzz의 많은 부분을 작성했으며, 현재 브라우저에서 아랍어 글자를 올바르게 그리는 셰이핑 엔진에 기여함
- Brill은 Semitics 카탈로그에 필요한 음역 문자를 모두 포함하기 위해 John Hudson에게 Brill 서체를 의뢰했고, 2011년 비상업용 무료로 공개함
- Sakhr AX-170은 1984년경 ROM에서 아랍어를 표시한 Saudi-Kuwaiti MSX 컴퓨터였고, 오른쪽에서 왼쪽으로 쓰는 Arabic BASIC 식별자를 지원함
- HarfBuzz, Amiri, Scheherazade, GNU Unifont의 Presentation Forms 지원, Noto Arabic, W3C Arabic Layout 문서는 소수의 개인·단체·자원봉사자 노력에 크게 의존함
- 브라우저 벤더는 HarfBuzz가 무료로 완성된 뒤 받아들였지만, 필사 전통의 정렬 방식을 화면에서 구현할 레이아웃 루프에는 거의 기여하지 않음
- 남은 격차는 몇몇 레이아웃 엔진에서 구현해야 하는 잘 이해된 알고리듬이며, 고객 대시보드의 들쭉날쭉한 왼쪽 여백은 이 투자 부재가 사용자에게 보이는 형태임
댓글과 토론
Lobste.rs 의견들
-
정말 대단한 글임
그리고 이 글자﷽가 코드 포인트 하나라는 점이 마음에 듦. 복사해보면 신기함
뜻은 “가장 자비롭고 가장 자애로우신 알라의 이름으로”라고 함﷽에 대해 글의 이 문장은 시적임
“렌더링 엔진을 아무도 믿지 못해서 렌더링이 인코딩에 구워져 있던 시대의 기념비. 낭송하는 파리가 호박 속에 영원히 보존된 것처럼”- 그 Unicode 문자는 여기서도 다뤄졌음: https://lobste.rs/s/7s4sjp
여기서 참조된 Wikipedia 링크의 참고문헌을 따라가 보는 재미가 있었음: https://lobste.rs/c/dq2ucz
요약하면, 이 문자가 Unicode에 들어간 이유는 파키스탄 코드 페이지에 있었기 때문이고, 거기에 들어간 이유는 법률 문서에 해당 문구를 포함해야 하는 법적 요구가 있었기 때문임. Urdu는 인도유럽어족 언어라 당시 기술로는 Basmallah를 쓰기 위해 Arabic 코드 페이지로 “외부 호출”하듯 전환하기가 어려웠을 것임
아쉽게도 모든 댓글이 그 커뮤니티의 장점을 보여주지는 않음
-
메타: 이런 글에는
typography태그가 필요하다는 또 하나의 훌륭한 예시임- 왜 필요한지 모르겠음. 주제가 꽤 인기 있어 보이는데, 이런 종류의 콘텐츠를 숨기려는 강한 수요가 있는 건가?
lobste.rs에서 태그는 주로 필터링용임
- 왜 필요한지 모르겠음. 주제가 꽤 인기 있어 보이는데, 이런 종류의 콘텐츠를 숨기려는 강한 수요가 있는 건가?
-
IE의
text-justify속성이라니, 그 시절엔 흥미로운 것들이 많았음.text-justify: newspaper도 있었는데, 수십 년 뒤 일부는 이것을 Knuth-Plass나 비슷한 것으로 설명했지만 실제로 그랬다고는 믿지 않음
https://mediumwell.com/wp-content/uploads/… 는 당시text-justify: newspaper라고 주장한 동작이 요즘 명세의text-justify: inter-character와 맞아떨어지는 모습을 보여줌
IE는 정말 꽤 이른 시기에 멋진 기능을 많이 갖고 있었고, 다른 브라우저들은 그런 기능을 “너무 어려움” 바구니에 방치했음. 결국 돌아오지 않았거나, 15년 혹은 30년이 지나서야 돌아온 것들도 있음. Firefox는 2017년에text-justify: inter-character를 얻었고, Chromium은 몇 달 전에야 그 부분을 구현했으며, Safari는 아직도 없음 -
엄청나게 훌륭하고 유익한 글임. 전체 이야기에 넓은 맥락을 제공하는 역사적 배경이 특히 좋았음
역사 관련 학위와 IT 경력을 둘 다 가진 입장에서는 두 관심사를 완벽하게 건드린 글이었음 -
글 어딘가가 내 LLM 감지기를 울리는데, 아쉬움. 깊이도 있고 현대 기술 스택에서 덜 문서화된 부분을 다루고 있기 때문임
LLM스럽게 읽히는 예시는 이런 부분인데, 글 전반에 걸쳐 그런 느낌이 있음:
“어떤 브라우저도 이를 제공하지 않는 이유는 구조적이며, 장애물로서 그 구조는 꽤 우아하다. Latin 정렬은 셰이핑된 텍스트를 고정된 것으로 다루고, 단어를 측정하고, 남는 공간을 간격에 붓고, 끝낸다. 셰이핑과 레이아웃은 각자의 상자 안에 머물며, 운영 중인 모든 텍스트 스택은 그 분리를 중심으로 설계되어 있다. Kashida 정렬은 그 상자를 열어젖힌다.”
@lr0에게 묻고 싶은데, 이 글의 본문이 LLM으로 생성·다듬기·번역된 것인지 궁금함. 그렇다면 최종 출력에 대해 LLM이 갖는 통제 수준을 조정하는 편이 좋을 수 있음. 예전 블로그 글들, 예를 들어 https://lr0.org/blog/p/gpt/ 와 https://lr0.org/blog/p/linux_new_users/ 는 훨씬 더 사람답게 느껴졌음