기술 문서

HotPDF의 RtLTextOut: Delphi의 오른쪽에서 왼쪽 PDF 텍스트

아랍어 문장 يوضح ملف PDF هذا를 일반 TextOut으로 보내면 돌아오는 페이지는 동시에 두 가지 면에서 잘못되었습니다. 단어들은 오른쪽에서 왼쪽이 아니라 왼쪽에서 오른쪽으로 흐르고, 글자들은 연결된 단어로 결합되는 대신 분리된 형태로 따로따로 앉아 있습니다. 오류는 나지 않습니다. Delphi는 컴파일하고 파일은 열리며 아랍어를 읽는 검토자는 결과물을 사용할 수 없다고 말해 줍니다. 픽스는 라이브러리 교체가 아니라 호출 하나입니다. HotPDF는 오른쪽에서 왼쪽으로 쓰는 텍스트를, 일반 TextOut이 수행하지 않는 재정렬을 처리하는 별도의 메소드 RtLTextOut을 통해 경로를 지정합니다. 이 페이지는 서명과 매개변수, 스크립트를 선택하는 문자셋 인수, 문서 수준의 부작용, 먼저 와야 하는 글꼴 설정, 실제로 지원에 도달하는 실패 및 각각의 수정 등 해당 메소드에 대한 작업 참조입니다

서명 및 매개변수

procedure RtLTextOut(X, Y: Single; angle: Extended;
  Text: WideString); overload;
procedure RtLTextOut(X, Y: Single; angle: Extended;
  Text: PWORD; TextLength: Integer); overload;

XY는 Y가 위로 증가하면서 왼쪽 하단에서 측정된 페이지 자체의 좌표 시스템에서 실행을 고정하며 모든 TextOut 호출이 사용하는 것과 동일한 원점입니다. RtLTextOut은 글리프 순서를 변경하지, 페이지가 측정하는 위치를 변경하지 않습니다. angleTextOut에서 작동하는 것과 정확히 동일하게 기준선을 회전하므로 0은 수평선을 그립니다. Text는 타이핑하는 논리적 순서로 작성된 문자열이며 두 번째 오버로드는 명시적인 코드 단위 카운트가 있는 가공되지 않은 PWORD 버퍼와 동일한 UTF-16 데이터를 가져오며, 이는 Delphi 문자열이 아니라 API에서 텍스트가 도착할 때 사용할 양식입니다. 이 유형들에 대한 오버로드 해결에 앞선 예전의 Delphi 버전에서 이 문자열 양식은 동일한 매개변수 목록을 가지고 RtLTextOutStr이라는 이름 하에 노출되어 있습니다

두 개의 출력 호출 간의 노동 분할은 엄격합니다. TextOut은 코드 포인트를 전달한 순서대로 그립니다. 이는 라틴어, 키릴어 및 CJK에 대해서는 올바르고 아랍어 및 히브리어에 대해서는 잘못된 것입니다. RtLTextOut은 내장된 라틴어 단어와 숫자를 줄 내부에서 왼쪽에서 오른쪽으로 유지하면서 각 줄을 먼저 시각적인 오른쪽에서 왼쪽 순서로 재정렬한 다음 그립니다. HotPDF는 문자에서 방향을 추측하는 대신 두 메서드를 의도적으로 분리해 유지하므로 호출할 메서드의 선택이 귀하가 얻을 스크립트 동작의 선택입니다. 오른쪽에서 왼쪽 실행에는 RtLTextOut을 사용하고, 다른 모든 항목에는 TextOut을 사용하며 한 메서드를 다른 메서드를 통하여 루트시키지 마십시오. 재정렬이 존재하는 이유, 유니코드 양방향 알고리즘 및 아랍어 문맥 연결이 실제로 하는 일, HotPDF 셰이핑이 중지되는 위치에 대한 동반 기사는 HotPDF를 사용한 아랍어 및 RTL 텍스트 셰이핑에 있습니다. 아래의 모든 내용은 실제 설정입니다

PDF에 그리기 전에 혼합된 아랍어 및 라틴어 줄을 시각적 오른쪽에서 왼쪽 순서로 재정렬하는 RtLTextOut 방법의 다이어그램
RtLTextOut은 그리기 전에 각 줄을 시각적 순서로 재정렬합니다. 오른쪽에서 왼쪽 실행은 줄 안의 내장된 라틴어 단어와 숫자가 왼쪽에서 오른쪽으로 읽히는 동안 순서를 유지합니다.

문자셋 인수가 스크립트를 결정합니다

아랍어를 레이아웃할지 아니면 히브리어를 레이아웃할지를 RtLTextOut에 알려주는 것은 메소드가 아니라 글꼴입니다. SetFont는 네 번째 인수로 Windows 문자셋을 취하고 해당 값은 스크립트 규칙을 오른쪽에서 왼쪽 호출로 전달합니다. 178은 아랍어를 선택하고 177은 히브리어를 선택합니다. 문자셋을 설정한 다음 그리면 그 아래의 두 줄은 더 이상의 구성 없이 올바른 읽기 순서로 나옵니다

// Arabic: charset 178 tells RtLTextOut to apply Arabic rules
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');

// Hebrew: charset 177 switches the rules to Hebrew
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
Pdf.CurrentPage.RtLTextOut(400, 660, 0, 'קובץ PDF זה');

하나의 배열 순서 세부 사항은 놓치기 쉽습니다. 문자셋이 포함된 현재 글꼴은 페이지 넘기기 후에도 남아있지 않기 때문에 SetFont가 먼저 와야 하고 모든 AddPage 후에 반복되어야 합니다. 반복하는 것을 잊으면 두 번째 페이지는 활성 글꼴이 무엇이든 그곳으로 떨어지는데 아랍어의 경우 이는 보통 빈 상자를 뜻합니다

여러분이 이미 뒤집은 텍스트는 되돌리지 않습니다

여기서 디버깅 시간을 가장 많이 잡아먹는 단 하나의 실수는 손으로 이미 뒤집은 문자열을 RtLTextOut에 먹이는 것입니다. 사람들은 일반적인 TextOut으로 시도하여 거꾸로 나온 후 이 방법으로 옵니다. 일반적인 임시방편은 그리기 전에 코드에서 문자를 뒤집는 것입니다. RtLTextOut은 자체적으로 내부적으로 뒤집으므로, 사전에 뒤집은 문자열은 한 번 더 뒤집히고 바로 시작한 곳으로 돌아가 떨어집니다. 텍스트를 입력하고 소리내어 읽을 순서인 논리적 순서로 전달하고, 호출이 재정렬을 수행하도록 합니다

트랩은 단순히 뒤집는 것보다 훨씬 심합니다. 왜냐하면 이중으로 역전된 문자열이 아랍어로만 구성된 한 테스트 구절에는 맞게 보이다가, 선이 라틴어 단어나 숫자를 전달하는 순간 깨지기 때문입니다. 오른쪽에서 왼쪽 선의 내부에 내장된 실행은 왼쪽에서 오른쪽으로 읽혀야 하는 것인데, 수동으로 뒤집는 것은 완전한 아랍어 케이스는 무사하게 넘어가는 동안 그 중첩을 망가뜨립니다. 그리하여 귀하의 첫 스모크 테스트를 버그가 유유히 통과하고 그 안에 계정 번호가 있는 진짜 인보이스에서 나중에 표면화됩니다. 여러분이 RtLTextOut으로 전환하는 순간 매 수동 역전을 모조리 걷어내십시오

알아둘 가치가 있는 Direction 부작용

RtLTextOut 호출은 현재 그리고 있는 선 이상을 변경합니다. 또한 문서의 읽기 방향 선호를 오른쪽에서 왼쪽으로 뒤집는데, 이는 그렇지 않다면 Direction 속성을 통해 여러분이 설정했을 내용과 동일한 사항입니다. 해당 세터는 문서의 ViewerPreferencesvpDirection을 추가하여, 뷰어에게 2-up 스프레드를 배열하는 방법과 마주보는 페이지 레이아웃이 시작되는 쪽을 알려줍니다. 전체 문서가 아랍어이거나 히브리어일 때는 이것이 정확히 원하는 것이며 공짜로 얻는 셈입니다

이것은 단일 페이지에서는 보이지 않기 때문에 알아두는 것이 가치가 있습니다. 문서가 대부분 왼쪽에서 오른쪽으로 읽히고 오른쪽에서 왼쪽 블록이 하나만 있는 경우에도, 처음 RtLTextOut을 호출하면 파일 전체의 환경설정이 기울어지고 어느 1페이지짜리 프루프도 그것을 보여주지 않을 것입니다. 누군가 양면 소책자를 인쇄하고 스프레드가 거울상으로 나올 때 몇 주 후에 증상이 나타납니다. 그것을 원하지 않는다면 오른쪽에서 왼쪽 실행을 한 뒤 Direction을 원상태로 명확히 되돌리십시오

// RtLTextOut already set the document direction to RightToLeft;
// restore left-to-right if the document is predominantly LTR
Pdf.Direction := LeftToRight;

진정으로 오른쪽에서 왼쪽으로 읽히는 문서의 경우 그대로 둡니다. 요점은 호출에 문서 전체적인 효과가 있어 소책자 서프라이즈가 절대 생기지 않도록 하는 것입니다

설치되어 있기를 바라는 폰트가 아닌, 함께 출하하는 글꼴을 등록하십시오

그릴 글리프가 글꼴에 없으면 재정렬이 아무런 소용이 없습니다. 전형적인 실패는 개발자의 기계에서는 Arial Unicode MS가 우연히 존재해서 흠집 하나 없이 렌더링되는 보고서가, 아랍어를 전혀 포괄하지 않는 폰트로 Windows가 몰래 대체한 고객의 서버에서는 줄지은 빈 상자로 나오는 경우입니다. 치유책은 설치된 시스템 폰트를 신뢰하지 않고 애플리케이션과 함께 출하하는 글꼴을 등록하는 것입니다

// Ship a known Arabic font and register it before drawing
Pdf.RegisterUnicodeTTF('C:\Fonts\NotoSansArabic.ttf');
Pdf.CurrentPage.SetFont('NotoSansArabic', [], 12, 178);
Pdf.CurrentPage.RtLTextOut(400, 700, 0, 'يوضح ملف PDF هذا');

두 가지 경계가 등록과 함께 적용됩니다. RegisterUnicodeTTF를 통해 들어온 글꼴이 임베딩되며, HotPDF의 포함된 유니코드 처리는 문서가 PDF 1.5 이상에 있을 필요가 있습니다. 다운스트림의 어딘가가 PDF 1.4를 고집할 때에만 문제가 발생하며, 문제가 발생할 때의 실패는 조용합니다. 다른 하나는 기술적이라기보다는 법률적인 문제입니다. TrueType 파일에는 포함 권한 비트가 수반되며, 화면에서는 좋아보이는 글씨체라도 고객 문서 내부에 이를 함께 선적하는 것을 금지하는 방법으로 허가되었을 수도 있습니다. 고객의 불만이 터진 후가 아니라 포함하기 전에 라이센스를 확인하십시오

완전한 콘솔 예제

모든 조각을 맞춰, 여기에 아랍어 줄 1개, 히브리어 줄 1개, 라틴어 제품명이 실린 혼합 줄이 있는 한 페이지를 작성하는 독립적인 프로그램이 있습니다. 각 블록은 해당 문자셋을 설정한 다음 논리적 순서로 그립니다

program RtLTextOutDemo;

{$APPTYPE CONSOLE}

uses
  HPDFDoc;   // HotPDF main unit

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'RtLTextOut.pdf';
    Pdf.BeginDoc;

    // A Latin heading goes through the ordinary TextOut path
    Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
    Pdf.CurrentPage.TextOut(40, 780, 0, 'Right-to-left text with HotPDF');

    // Arabic: charset 178, logical order, RtLTextOut does the reordering
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
    Pdf.CurrentPage.RtLTextOut(400, 720, 0,
      'يوضح ملف PDF هذا كيفية التعامل مع النص العربي.');

    // Hebrew: charset 177
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 177);
    Pdf.CurrentPage.RtLTextOut(400, 680, 0,
      'קובץ PDF זה מדגים טקסט עברי הזורם מימין לשמאל.');

    // Mixed line: the embedded Latin word still reads left to right
    Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 12, 178);
    Pdf.CurrentPage.RtLTextOut(400, 640, 0,
      'مرحبا بالعالم! تم إنشاؤه بواسطة HotPDF');

    Pdf.EndDoc;
    Writeln('Wrote RtLTextOut.pdf');
  finally
    Pdf.Free;
  end;
end.

이를 실행하고 결과를 엽니다. 아랍어와 히브리어 줄은 오른쪽에서 왼쪽으로 읽히고, 스크립트가 글자를 연결하는 곳에서 문자가 연결되며, 마지막 줄에서 HotPDF 토큰은 아랍어 실행 내에서 왼쪽에서 오른쪽으로 앉아 있습니다. 비록 처음 리뷰어가 흔히 버그로 신고할지라도 그 중첩은 올바른 양방향 결과이며 버그가 아닙니다. 위에서 연결한 셰이핑 기사는 유니코드 규칙에서 이를 요구하는 이유와 결코 보고되지 않도록 수용 기준을 설명하는 방법을 설명합니다

일반적인 오류 및 그 수정

아래의 모든 실패는 실제 지원 스레드에 나타났으며 각각은 위의 섹션 중 하나로 거슬러 올라갑니다

  • 출력이 뒤로 읽히거나 혼합된 줄에서 스크램블됨 — 문자열이 호출 전에 손으로 뒤집혔으며 대개 TextOut 시도에서 남은 임시방편입니다. 수동적인 역전을 전부 삭제하고 논리적 순서를 통과하십시오. RtLTextOut은 내부적으로 뒤집습니다
  • 문자가 고립된 형태로 분리되어 인쇄됨 — 일반적인 TextOut을 통과했거나, SetFont가 오른쪽에서 왼쪽으로 가는 문자셋이 없이 호출된 텍스트. RtLTextOut으로 그리고 아랍어에 178을 또는 네 번째 SetFont 인수로 히브리어에 177을 전달하십시오
  • 고객의 기계의 빈 상자 — Windows가 아랍어나 히브리어를 지원하지 않는 폰트로 교체함. 설치된 폰트를 꼽는 것을 멈추십시오. RegisterUnicodeTTFSetFont를 통해 이름으로 함께 출하하는 서체를 등록하십시오
  • 두 번째 페이지가 잘못된 글꼴로 렌더링됨 — 현재 폰트는 AddPage 후 살아남지 못함. 모든 페이지가 나뉜 후에 문자셋을 포함하여 SetFont를 반복하십시오
  • LTR 위주의 문서에서 이중 스프레드가 거울상으로 인쇄됨 — 첫 RtLTextOut 호출이 문서의 Direction을 부작용으로 뒤집음. 오른쪽에서 왼쪽 실행을 한 뒤에 Pdf.Direction := LeftToRight를 설정하십시오
  • 임베디드 유니코드 텍스트가 다운스트림에서 조용히 디그레이드함 — 파이프라인의 어딘가에서 PDF 1.4를 강제하며, HotPDF의 내장된 유니코드 처리는 1.5 이상이 필요함. 문서 버전을 높이거나 다운스트림의 제약을 제거하십시오

형식이 출하되기 전에 눈대중 너머로 확인하십시오. 뷰어 밖으로 텍스트를 다시 복사하고, 문서 내부 검색을 실행하고, 여러분의 개발 폰트가 없는 기계에서 파일을 열고, 진짜 문서 1부를 모국어 독자 앞에 두십시오. 전체 검증 체크리스트, 스크립트별 커버리지 맵 및 구축할 가치가 있는 문자열 테스트 코퍼스 등 모두가 HotPDF가 있는 아랍어 및 RTL 텍스트 셰이핑에 대한 동반 기사에 있습니다

여기에 표시된 RtLTextOut, SetFontRegisterUnicodeTTF 호출은 Delphi 및 C++Builder용 HotPDF Component의 일부입니다