2008-09-24 26 views
29

Chương trình của tôi tạo các tài liệu PDF tương đối đơn giản theo yêu cầu, nhưng tôi gặp sự cố với các ký tự unicode, như ký tự kanji hoặc ký tự toán học lẻ. Để viết một chuỗi bình thường trong PDF, bạn đặt nó trong dấu ngoặc:Unicode trong PDF

(something) 

Ngoài ra còn có tùy chọn để thoát khỏi một nhân vật với mã bát phân:

(\527) 

nhưng điều này chỉ đi lên đến 512 ký tự. Làm cách nào để mã hóa hoặc thoát các ký tự cao hơn? Tôi đã nhìn thấy các tham chiếu đến các luồng byte và các chuỗi được mã hóa bằng hex, nhưng không có tham chiếu nào tôi đã đọc có vẻ sẵn sàng cho tôi biết cách thực sự làm như thế.


Edit: Ngoài ra, chỉ cho tôi một Java thư viện PDF tốt mà sẽ làm công việc cho tôi. Cái tôi đang sử dụng là phiên bản của gnujpdf (mà tôi đã sửa một số lỗi trong đó, vì tác giả gốc đã xuất hiện AWOL), cho phép bạn lập trình chống lại giao diện đồ họa AWT, và lý tưởng là bất kỳ sự thay thế nào cũng nên làm giống nhau.

Các lựa chọn thay thế có vẻ là HTML -> PDF hoặc mô hình có lập trình dựa trên các đoạn văn và các hộp cảm thấy rất giống HTML. iText là một ví dụ về sau này. Điều này có nghĩa là viết lại mã hiện tại của tôi, và tôi không tin rằng họ sẽ cho tôi sự linh hoạt tương tự khi đặt ra.


Chỉnh sửa 2: Tôi đã không nhận ra trước đó, nhưng các thư viện iText có một API Graphics2D và dường như để xử lý unicode một cách hoàn hảo, vì vậy đó là những gì tôi sẽ được sử dụng. Mặc dù nó không phải là một câu trả lời cho câu hỏi như được hỏi, nó giải quyết vấn đề cho tôi.


Sửa 3: iText đang làm việc độc đáo cho tôi. Tôi đoán bài học là, khi phải đối mặt với một cái gì đó dường như vô nghĩa khó khăn, hãy tìm ai đó biết nhiều hơn về nó hơn bạn.

+5

Ngoài việc gói các chuỗi bằng '()', bạn cũng có thể sử dụng '<>'. Trong gt/lt, bạn sử dụng số hex thay vì chữ cái. Ít hiệu quả hơn nhiều, nhưng bạn không cần lo lắng về việc trốn thoát. '': "Xin chào thế giới!" như một chuỗi Unicode-16. Bài viết của Plinth cũng rất quan trọng ... bạn PHẢI sử dụng FE FF. FFFE là xấu. Đối với một số lý do. :/ –

Trả lời

10

Câu trả lời đơn giản là không có câu trả lời đơn giản. Nếu bạn nhìn vào đặc tả PDF, bạn sẽ thấy toàn bộ chương - và một chương dài - dành cho các cơ chế hiển thị văn bản. Tôi đã thực hiện tất cả các hỗ trợ PDF cho công ty của tôi, và xử lý văn bản là một phần phức tạp nhất của bài tập. Giải pháp bạn phát hiện - sử dụng thư viện của bên thứ ba để thực hiện công việc cho bạn - thực sự là lựa chọn tốt nhất, trừ khi bạn có các yêu cầu đặc biệt, đặc biệt cho các tệp PDF của mình.

-1

Tôi không phải là một chuyên gia về PDF, và (như Ferruccio nói) các thông số kỹ thuật PDF của Adobe sẽ cho bạn biết tất cả mọi thứ, nhưng một ý nghĩ hiện lên trong tâm trí tôi:

Bạn có chắc chắn bạn đang sử dụng một phông chữ mà hỗ trợ tất cả các ký tự bạn cần?

Trong ứng dụng của chúng tôi, chúng tôi tạo PDF từ các trang HTML (với một thư viện của bên thứ ba), và chúng tôi có vấn đề này với ký tự Cyrillic ...

+0

Chúng tôi đang gắn bó với các phông chữ cơ bản trên mọi máy tính và không nhúng bất kỳ phông chữ nào. –

+1

"Thông số kỹ thuật PDF tại Adobe sẽ cho bạn biết mọi thứ". Thật không may, theo kinh nghiệm của tôi, họ không biết. – Renan

+2

@Renan: "Thông số kỹ thuật PDF tại Adobe sẽ cho bạn biết mọi thứ". Thật không may, theo kinh nghiệm của tôi, bạn không thấy chúng dễ dàng và chúng thường phức tạp một cách không cần thiết. – Algoman

3

Xem Phụ lục D (trang 995) của đặc tả PDF. Có một số giới hạn các phông chữ và bộ ký tự được xác định trước trong ứng dụng PDF của người tiêu dùng. Để hiển thị các ký tự khác, bạn cần nhúng một phông chữ có chứa chúng. Bạn cũng chỉ nên nhúng một tập con của phông chữ, chỉ bao gồm các ký tự bắt buộc để giảm kích thước tệp. Tôi cũng đang làm việc để hiển thị các ký tự Unicode trong PDF và nó là một rắc rối lớn.

Kiểm tra PDFBox hoặc iText.

http://www.adobe.com/devnet/pdf/pdf_reference.html

28

Trong PDF tham khảo trong chương 3, đây là những gì họ nói về Unicode:

chuỗi văn bản được mã hóa trong một trong hai PDFDocEncoding hoặc Unicode mã hóa ký tự. PDFDocEncoding là một bộ mã hóa của ISO Latin 1 và được ghi trong Phụ lục D. Unicode được mô tả trong tiêu chuẩn Unicode của Unicode Consortium (xem Tài liệu tham khảo). Đối với các chuỗi văn bản được mã hóa bằng Unicode, hai byte đầu tiên phải là 254 theo sau là 255. Hai byte này biểu thị điểm đánh dấu byte Unicode, U + FEFF, cho biết rằng chuỗi được mã hóa trong UTF-16BE (big-endian) lược đồ mã hóa được chỉ định theo tiêu chuẩn Unicode. (Cơ chế này ngăn cản việc bắt đầu một chuỗi bằng cách sử dụng PDFDocEncoding với hai ký tự gai ydieresis, không chắc là là một khởi đầu có ý nghĩa của một từ hoặc cụm từ).

+0

Đây là một trích xuất cực kỳ hữu ích. Cảm ơn bạn! –

+10

Tôi biết điều này nghe có vẻ quá tốt là đúng. "Chuỗi văn bản" được sử dụng cho siêu dữ liệu tài liệu (chú thích, tên dấu trang), ** không ** cho văn bản được hiển thị! –

+0

@BrechtMachiels Ít nhất trong tài liệu tham khảo PDF 1.7, toán tử hiển thị văn bản đối tượng văn bản ('BT') (' Tj') nói rõ ràng "Hiển thị chuỗi văn bản". Có nghĩa là chúng có thể được mã hóa UTF-16BE như được mô tả. – jdmichal

3

Tôi đã làm việc vài ngày về chủ đề này ngay bây giờ và những gì tôi đã học được rằng unicode là (tốt như) không thể trong pdf. Sử dụng các ký tự 2 byte theo cách mà mô tả thứ chín chỉ hoạt động với CID-Phông chữ.

dường như, CID-phông chữ là một cấu trúc nội bộ pdf và chúng không thực sự là phông chữ theo nghĩa đó - chúng dường như giống như đồ họa con hơn, có thể được gọi bằng cách giải quyết chúng (với địa chỉ 16 bit).

Vì vậy, để sử dụng unicode trong pdf trực tiếp

  1. bạn sẽ phải chuyển đổi phông chữ bình thường để CID-Fonts, mà có lẽ là vô cùng khó khăn - bạn sẽ phải tạo ra thói quen đồ họa từ font gốc (?), trích xuất các chỉ số nhân vật, v.v.
  2. bạn không thể sử dụng phông chữ CID như phông chữ thông thường - bạn không thể tải hoặc chia tỷ lệ phông chữ theo cách bạn tải và chia tỷ lệ phông chữ bình thường
  3. không gian Unicode đầy đủ

IMHO, những điểm này làm cho nó hoàn toàn không khả thi khi sử dụng unicode trực tiếp.



Những gì tôi đang làm thay vì hiện đang sử dụng các nhân vật gián tiếp theo cách sau: Đối với mỗi font chữ, tôi tạo ra một bảng mã (và một tra cứu-bảng để tra cứu nhanh) - trong C++ đây sẽ là một cái gì đó giống như

std::map<std::string, std::vector<wchar_t> > Codepage; 
std::map<std::string, std::map<wchar_t, int> > LookupTable; 

sau đó, bất cứ khi nào tôi muốn đặt một số unicode dây trên một trang, tôi lặp nhân vật của mình, nhìn chúng trong việc tra cứu-bàn và - nếu họ là người mới, tôi thêm chúng đến trang mã như sau:

for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++) 
{     
    if(LookupTable[fontname].find(*i) == LookupTable[fontname].end()) 
    { 
     LookupTable[fontname][*i] = Codepage[fontname].size(); 
     Codepage[fontname].push_back(*i); 
    } 
} 

sau đó, tôi tạo ra một chuỗi mới, nơi mà các ký tự từ chuỗi ban đầu được thay thế bằng vị trí của họ trong bảng mã như thế này: "H € llo World"

static std::string hex = "ABCDEF"; 
std::string result = "<"; 
for(std::wstring::const_iterator i = str.begin(); i != str.end(); i++) 
{     
    int id = LookupTable[fontname][*i] + 1; 
    result += hex[(id & 0x00F0) >> 4]; 
    result += hex[(id & 0x000F)]; 
} 
result += ">"; 

ví dụ, có thể trở thành < 01020303040506040703080905> và bây giờ bạn có thể chỉ cần đặt chuỗi đó vào pdf và in nó, sử dụng toán tử Tj như bình thường ...

nhưng bây giờ bạn có vấn đề: pdf không biết rằng bạn có nghĩa là "H" vào ngày 01. Để giải quyết vấn đề này, bạn cũng phải bao gồm bảng mã trong tệp pdf. Này được thực hiện bằng cách thêm một /Encoding đến đối tượng Font và thiết khác biệt

của nó Đối với "H € llo World!" Ví dụ, đây Font-Object sẽ làm việc:

5 0 obj 
<< 
    /F1 
    << 
     /Type /Font 
     /Subtype /Type1 
     /BaseFont /Times-Roman 
     /Encoding 
     << 
      /Type /Encoding 
      /Differences [ 1 /H /Euro /l /o /space /W /r /d /exclam ] 
     >> 
    >> 
>> 
endobj 

tôi tạo ra nó với mã này:

ObjectOffsets.push_back(stream->tellp()); // xrefs entry 
(*stream) << ObjectCounter++ << " 0 obj \n<<\n"; 
int fontid = 1; 
for(std::list<std::string>::iterator i = Fonts.begin(); i != Fonts.end(); i++) 
{ 
    (*stream) << " /F" << fontid++ << " << /Type /Font /Subtype /Type1 /BaseFont /" << *i; 

    (*stream) << " /Encoding << /Type /Encoding /Differences [ 1 \n"; 
    for(std::vector<wchar_t>::iterator j = Codepage[*i].begin(); j != Codepage[*i].end(); j++) 
     (*stream) << " /" << GlyphName(*j) << "\n"; 
    (*stream) << " ] >>"; 

    (*stream) << " >> \n"; 
} 
(*stream) << ">>\n"; 
(*stream) << "endobj \n\n"; 

Chú ý rằng tôi sử dụng một toàn cầu font-đăng ký - Tôi sử dụng các tên phông chữ tương tự/F1,/F2, ... trong toàn bộ tài liệu pdf. Cùng một đối tượng đăng ký phông chữ được tham chiếu trong mục /Resources Nhập tất cả các trang. Nếu bạn thực hiện điều này một cách khác nhau (ví dụ: bạn sử dụng một phông chữ đăng ký trên mỗi trang) - bạn có thể phải điều chỉnh mã cho tình huống của bạn ...

Vậy làm thế nào để bạn tìm thấy tên của hình tượng (/ Euro cho " € ",/exclam cho"! ", V.v.)? Trong đoạn mã trên, điều này được thực hiện bằng cách đơn giản gọi "GlyphName (* j)". Tôi đã tạo ra phương pháp này với một BASH-Script từ danh sách tìm thấy tại

http://www.jdawiseman.com/papers/trivia/character-entities.html

và nó trông như thế này

const std::string GlyphName(wchar_t UnicodeCodepoint) 
{ 
    switch(UnicodeCodepoint) 
    { 
     case 0x00A0: return "nonbreakingspace"; 
     case 0x00A1: return "exclamdown"; 
     case 0x00A2: return "cent"; 
     ... 
    } 
} 

Một vấn đề lớn Tôi đã bỏ ngỏ là chỉ này hoạt động miễn là bạn sử dụng tối đa 254 ký tự khác nhau từ cùng một phông chữ. Để sử dụng hơn 254 ký tự khác nhau, bạn sẽ phải tạo nhiều mã cho cùng một phông chữ. Bên trong pdf, các codepages khác nhau được thể hiện bằng các phông chữ khác nhau, vì vậy để chuyển đổi giữa các mã, bạn sẽ phải chuyển đổi phông chữ, mà về lý thuyết có thể thổi pdf lên một chút, nhưng tôi cho một, có thể sống với điều đó. ..

+0

bằng cách này - danh sách các hình tượng tôi đã đề cập, chứa hơn 3600 mục nhập. Tệp mã được tạo là 175 KiB và tệp đối tượng được biên dịch là 600 KiB lớn (1,1 MiB trong phiên bản gỡ lỗi) – Algoman

+0

Ngay sau khi bạn bắt đầu sử dụng phông chữ khác với phông chữ chuẩn 14, phông chữ CID có thể trở nên khá tự nhiên. – mkl

+1

* 1. bạn sẽ phải chuyển đổi phông chữ thông thường thành CID-Phông chữ, có thể cực kỳ khó * - điều này khá đơn giản đối với các phông chữ OpenType (với CFF hoặc TrueType).Chúng có thể được bao gồm dưới dạng 'CIDFontType0' (CFF) hoặc' CIDFontType2' (TrueType) bằng cách sử dụng mã hóa 'Identity-H'. Điều này tôi làm trong [rinohtype] (https://github.com/brechtm/rinohtype/blob/6e6b024e757eff57a8cef143710e667e0d2f365f/rinoh/backend/pdf/__init__.py#L75). –

4

Câu trả lời của Algoman là sai trong nhiều điều. Bạn có thể tạo một tài liệu PDF với unicode trong đó 'và nó không phải là một khoa học tên lửa, mặc dù nó cần một số công việc. Có, anh ấy đúng, để sử dụng nhiều hơn 255 ký tự trong một phông chữ, bạn phải tạo một đối tượng pdf phông chữ (CIDFont). Sau đó, bạn chỉ đề cập đến phông chữ TrueType thực mà bạn muốn sử dụng làm mục nhập DescendatFont của CIDFont. Bí quyết là sau đó bạn phải sử dụng chỉ số glyph của phông chữ thay vì mã ký tự. Để có được bản đồ chỉ mục này, bạn phải phân tích cú pháp phần cmap của phông chữ - nhận nội dung của phông chữ với hàm GetFontData và nắm lấy đặc điểm kỹ thuật TTF. Và đó là nó! Tôi đã làm nó và bây giờ tôi có một pdf unicode!

mẫu mã cho phân tích cmap phần là ở đây: https://support.microsoft.com/en-us/kb/241020

Và vâng, đừng quên/ToUnicode entry như @ user2373071 chỉ ra ngừơi tiêu dùng sẽ không thể để tìm kiếm PDF của bạn hoặc sao chép văn bản từ nó.

2

Như dredkin chỉ ra, bạn phải sử dụng chỉ số glyph thay vì giá trị ký tự Unicode trong luồng nội dung trang. Điều này là đủ để hiển thị văn bản Unicode trong PDF, nhưng văn bản Unicode sẽ không thể tìm kiếm được. Để làm cho văn bản có thể tìm kiếm được hoặc sao chép/dán vào nó, bạn cũng sẽ cần phải bao gồm một luồng/ToUnicode. Luồng này nên dịch từng glyph trong tài liệu thành ký tự Unicode thực tế.