Unicode in PDF

Il mio programma genera documenti PDF relativamente semplici su richiesta, ma ho problemi con caratteri unicode, come kanji o simboli matematici dispari. Per scrivere una stringa normale in PDF, inseriscila tra parentesi:

(something) 

C’è anche la possibilità di sfuggire a un personaggio con codici ottali:

 (\527) 

ma questo va solo fino a 512 caratteri. Come si codifica o si sfuggono personaggi superiori? Ho visto riferimenti a flussi di byte e stringhe con codifica esadecimale, ma nessuno dei riferimenti che ho letto sembra essere disposto a dirmi come farlo effettivamente.


Modifica: In alternativa, indicami una buona libreria Java PDF che farà il lavoro per me. Quello che sto attualmente usando è una versione di gnujpdf (che ho corretto diversi bug in, dal momento che l’autore originale sembra essere andato AWOL), che consente di programmare contro un’interfaccia grafica AWT, e idealmente qualsiasi sostituzione dovrebbe fare lo stesso.

Le alternative sembrano essere HTML -> PDF, o un modello programmatico basato su paragrafi e scatole che somiglia molto all’HTML. iText è un esempio di quest’ultimo. Ciò significherebbe riscrivere il mio codice esistente, e non sono convinto che mi darebbero la stessa flessibilità nella stenditura.


Modifica 2: non l’avevo capito prima, ma la libreria iText ha un’API Graphics2D e sembra gestire perfettamente l’unicode, quindi è quello che userò. Anche se non è una risposta alla domanda come chiesto, risolve il problema per me.


Modifica 3: iText funziona bene per me. Immagino che la lezione sia, di fronte a qualcosa che sembra inutilmente difficile, cercare qualcuno che ne sappia di più di te.

La semplice risposta è che non esiste una risposta semplice. Se date un’occhiata alle specifiche PDF, vedrete un intero capitolo – e uno lungo – dedicato ai meccanismi di visualizzazione del testo. Ho implementato tutto il supporto PDF per la mia azienda e la gestione del testo è stata di gran lunga la parte più complessa dell’esercizio. La soluzione che hai scoperto – usa una libreria di terze parti per fare il lavoro per te – è davvero la scelta migliore, a meno che tu non abbia requisiti specifici e specifici per i tuoi file PDF.

Nel riferimento PDF nel capitolo 3, questo è ciò che dicono di Unicode:

Le stringhe di testo sono codificate in PDFDocEncoding o nella codifica dei caratteri Unicode. PDFDocEncoding è un superset della codifica ISO Latin 1 ed è documentato nell’Appendice D. Unicode è descritto nello Standard Unicode dal Consorzio Unicode (vedi la Bibliografia). Per stringhe di testo codificate in Unicode, i primi due byte devono essere 254 seguiti da 255. Questi due byte rappresentano l’indicatore di ordine byte Unicode, U + FEFF, che indica che la stringa è codificata nello schema di codifica UTF-16BE (big-endian) specificato nello standard Unicode. (Questo meccanismo preclude l’inizio di una stringa utilizzando PDFDocEncoding con i due caratteri ydieresis, che è improbabile che sia un inizio significativo di una parola o frase).

La risposta di Algoman è sbagliata in molte cose. Puoi creare un documento PDF con unicode “e non è una scienza missilistica, anche se ha bisogno di un po ‘di lavoro. Sì, ha ragione, per utilizzare più di 255 caratteri in un font devi creare un object pdf composito con font (CIDFont). Quindi devi solo menzionare il vero font TrueType che vuoi usare come voce DescendatFont di CIDFont. Il trucco è che dopo devi usare gli indici glifi di un font invece dei codici carattere. Per ottenere questa mappa di indici devi analizzare la sezione cmap di un font – ottenere i contenuti del font con la funzione GetFontData e prendere in mano le specifiche TTF. E questo è tutto! L’ho appena fatto e ora ho un pdf unicode!

Il codice di esempio per l’analisi cmap sezione cmap è qui: https://support.microsoft.com/en-us/kb/241020

E sì, non dimenticare / voce ToUnicode come @ user2373071 sottolineato o l’utente non sarà in grado di cercare il tuo PDF o copiare il testo da esso.

Vedere l’Appendice D (pagina 995) della specifica PDF. Esiste un numero limitato di caratteri e set di caratteri predefiniti in un’applicazione consumer in formato PDF. Per visualizzare altri caratteri è necessario incorporare un carattere che li contenga. È anche preferibile incorporare solo un sottoinsieme del font, inclusi solo i caratteri richiesti, in modo da ridurre le dimensioni del file. Sto anche lavorando alla visualizzazione di caratteri Unicode in PDF ed è un grosso problema.

Controlla PDFBox o iText.

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

Come ha sottolineato Dredkin, è necessario utilizzare gli indici di glifo anziché il valore del carattere Unicode nel stream di contenuti della pagina. Questo è sufficiente per visualizzare il testo Unicode in PDF, ma il testo Unicode non sarebbe ricercabile. Per rendere il testo ricercabile o avere un lavoro di copia / incolla su di esso, dovrai anche includere un stream / ToUnicode. Questo stream dovrebbe tradurre ciascun glifo nel documento nel carattere Unicode effettivo.

Ho lavorato diversi giorni su questo argomento ora e quello che ho imparato è che l’unicode è (buono come) imansible in pdf. Utilizzando caratteri a 2 byte, il modo in cui il plinth descritto funziona solo con i caratteri CID.

apparentemente, i CID-Fonts sono un costrutto pdf-interno e in questo senso non sono proprio dei font – sembrano più come subroutine grafiche, che possono essere richiamate indirizzandole (con indirizzi a 16 bit).

Quindi per usare unicode in pdf direttamente

  1. dovresti convertire i caratteri normali in caratteri CID, che è probabilmente estremamente difficile – devi generare le routine grafiche dal carattere originale (?), estrarre le metriche dei caratteri, ecc.
  2. non puoi usare i caratteri CID come i caratteri normali – non puoi caricarli o ridimensionarli nel modo in cui carichi e riduci i caratteri normali
  3. inoltre, i caratteri a 2 byte non coprono nemmeno lo spazio Unicode completo

IMHO, questi punti rendono assolutamente irrealizzabile l’uso diretto di unicode.



Quello che sto facendo invece ora è usare i personaggi indirettamente nel modo seguente: per ogni font, io genero una tabella codici (e una tabella di ricerca per ricerche veloci) – in c ++ questo sarebbe qualcosa di simile

 std::map > Codepage; std::map > LookupTable; 

poi, ogni volta che voglio mettere unicode-string su una pagina, I iterare i suoi caratteri, cercarli nella tabella di ricerca e – se sono nuovi, li aggiungo alla code-page come questa:

 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); } } 

quindi, genero una nuova stringa, in cui i caratteri della stringa originale vengono sostituiti dalle loro posizioni nella codepage in questo modo:

 static std::string hex = "0123456789ABCDEF"; 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 += ">"; 

per esempio “H € llo World!” potrebbe diventare <01020303040506040703080905> e ora puoi mettere quella stringa nel pdf e farla stampare, usando l’operatore Tj come al solito …

ma ora hai un problema: il pdf non sa che vuoi dire “H” di 01. Per risolvere questo problema, devi anche includere la codepage nel file pdf. Questo viene fatto aggiungendo un / Encoding all’object Font e impostandone le Differenze

Per “H € llo World!” Ad esempio, questo object Font funzionerebbe:

 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 

Lo genero con questo codice:

 ObjectOffsets.push_back(stream->tellp()); // xrefs entry (*stream) << ObjectCounter++ << " 0 obj \n<<\n"; int fontid = 1; for(std::list::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::iterator j = Codepage[*i].begin(); j != Codepage[*i].end(); j++) (*stream) << " /" << GlyphName(*j) << "\n"; (*stream) << " ] >>"; (*stream) << " >> \n"; } (*stream) << ">>\n"; (*stream) << "endobj \n\n"; 

Si noti che uso un global font-register - utilizzo gli stessi nomi di font / F1, / F2, ... per tutto il documento pdf. Lo stesso object font-register è referenziato nella voce / Resources di tutte le pagine. Se lo fai in modo diverso (ad esempio, usi un solo font-register per pagina), potresti dover adattare il codice alla tua situazione ...

Quindi, come trovi i nomi dei glifi (/ Euro per "€", / esclam per "!" Ecc.)? Nel codice sopra, questo viene fatto semplicemente chiamando "GlyphName (* j)". Ho generato questo metodo con un BASH-Script dall'elenco trovato su

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

e sembra così

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

Un grosso problema che ho lasciato aperto è che funziona solo fino a quando usi al massimo 254 caratteri diversi dallo stesso font. Per utilizzare più di 254 caratteri diversi, è necessario creare più codepage per lo stesso carattere.

All'interno del pdf, le diverse codepage sono rappresentate da diversi tipi di carattere, quindi per passare da una codepage a un'altra, è necessario cambiare tipo di carattere, che potrebbe teoricamente far esplodere un po 'il pdf, ma io per primo, posso conviverci con questo ...

Non sono un esperto di PDF e (come ha detto Ferruccio) le specifiche PDF di Adobe dovrebbero dirti tutto, ma un pensiero mi è saltato in mente:

Sei sicuro di utilizzare un font che supporti tutti i caratteri che ti servono?

Nella nostra applicazione, creiamo PDF da pagine HTML (con una libreria di terze parti) e abbiamo riscontrato questo problema con i caratteri cirillici …