Unicode en PDF
mon programme génère des documents PDF relativement simples sur demande, mais j'ai des problèmes avec les caractères unicode, comme les kanji ou les symboles mathématiques impairs. Pour écrire une chaîne normale en PDF, placez-la entre parenthèses:
(something)
il y a aussi la possibilité d'échapper à un caractère avec des codes octal:
(7)
mais cela ne monte qu'à 512 caractères. Comment encoder ou échapper à des caractères plus élevés? J'ai vu des références à des flux d'octets et des chaînes encodées par hexadécimal, mais aucune des les références que j'ai lues semblent être prêtes à me dire comment le faire.
Edit: alternativement, dirigez-moi vers une bonne bibliothèque Java PDF qui fera le travail pour moi. Celui que j'utilise actuellement est une version de gnujpdf (dans laquelle j'ai corrigé plusieurs bugs, puisque l'auteur original semble avoir disparu), qui vous permet de programmer contre une interface graphique AWT, et idéalement tout remplacement devrait faire la même chose.
Les solutions de rechange semblent être soit HTML - > PDF, soit un modèle programmatique basé sur des paragraphes et des boîtes qui ressemble beaucoup à HTML. iText en est un exemple. Cela signifierait réécrire mon code existant, et je ne suis pas convaincu qu'ils me donneraient la même flexibilité dans la disposition.
Edit 2: Je n'avais pas réalisé avant, mais la bibliothèque iText a une API Graphics2D et semble traiter unicode parfaitement, donc c'est ce que je vais utiliser. Si ce n'est pas une réponse à la question posée, il résout le problème pour moi.
Edit 3: iText fonctionne bien pour moi. Je suppose que la leçon est que, face à quelque chose qui semble inutilement difficile, cherchez quelqu'un qui en sait plus que vous.
7 réponses
la réponse simple est qu'il n'y a pas de réponse simple. Si vous jetez un oeil à la spécification PDF, vous verrez un chapitre entier - et long - consacré aux mécanismes de l'affichage du texte. J'ai mis en place tout le support PDF pour mon entreprise, et le traitement de texte était de loin la partie la plus complexe de l'exercice. La solution que vous avez découverte - utiliser une bibliothèque tierce partie pour faire le travail pour vous - est vraiment le meilleur choix, à moins que vous ayez des exigences très spécifiques et spécifiques pour votre Les fichiers PDF.
dans la référence PDF au chapitre 3, c'est ce qu'ils disent à propos D'Unicode:
les chaînes de texte sont encodées soit PDFDocEncoding, soit Unicode character encoding. PDFDocEncoding est un surensemble de l'encodage ISO Latin 1 et est documenté à L'Annexe D. Unicode est décrit dans la norme Unicode par le Consortium Unicode (voir la bibliographie). Pour les chaînes de texte encodées en Unicode, les deux premiers octets doivent être 254 suivis par 255. Ces deux octets représentent le marqueur D'ordre des octets Unicode, U+FEFF, indiquant que la chaîne est encodée dans le schéma D'encodage UTF-16BE (big-endian) spécifié dans le standard Unicode. (Ce mécanisme empêche de lancer une chaîne en utilisant PDFDocEncoding avec les deux caractères thorn ydieresis, ce qui est peu probable être utile au début d'un mot ou d'une phrase).
la réponse D'Algoman est mauvais dans beaucoup de choses. Vous faites un document PDF avec unicode dedans et ce n'est pas une science de fusée, bien qu'il ait besoin de quelques travaux.
Oui il a raison, pour utiliser plus de 255 caractères dans une police, vous devez créer un objet PDF composite font (CIDFont).
Ensuite, vous mentionnez simplement la police TrueType réelle que vous voulez utiliser comme une entrée de DescendatFont de CIDFont.
L'astuce est qu'après que vous devez utiliser indices de glyphe d'une police au lieu de codes de caractères. Pour obtenir cette carte des indices vous devez analyser cmap
section de la police - obtenir le contenu de la police GetFontData
fonction et mettre la main sur la spécification TTF.
Et c'est tout! Je viens de le faire et maintenant j'ai un PDF unicode!
exemple de Code pour l'analyse cmap
l'article est ici: https://support.microsoft.com/en-us/kb/241020
Et oui, n'oubliez pas /ToUnicode entrée comme @user2373071 souligné ou de l'utilisateur ne sera pas en mesure de rechercher votre PDF ou copiez le texte de celui-ci.
voir L'annexe D (page 995) de la spécification PDF. Il y a un nombre limité de polices et de jeux de caractères prédéfinis dans une application grand public PDF. Pour afficher d'autres caractères que vous devez intégrer une police qui les contient. Il est également préférable de n'intégrer qu'un sous-ensemble de la police, incluant seulement les caractères requis, afin de réduire la taille du fichier. Je travaille également sur l'affichage des caractères Unicode en PDF et c'est un problème majeur.
consultez la boîte pdf ou iText.
comme dredkin l'a souligné, vous devez utiliser les indices de glyphe au lieu de la valeur de caractère Unicode dans le flux de contenu de la page. Cela est suffisant pour afficher le texte Unicode en PDF, mais le texte Unicode ne serait pas consultable. Pour que le texte puisse être recherché ou que le copier/coller fonctionne, vous devrez aussi inclure un flux /ToUnicode. Ce flux devrait traduire chaque glyphe du document en caractère Unicode.
j'ai travaillé plusieurs jours sur ce sujet aujourd'hui, et ce que j'ai appris, c'est qu'unicode est (aussi bon que) impossible en pdf. En utilisant des caractères de 2 octets, la façon dont plinth décrit ne fonctionne qu'avec les polices CID.
apparemment, les polices CID sont une construction interne pdf et elles ne sont pas vraiment des polices dans ce sens - elles semblent être plus comme des graphismes-sous-programmes, qui peuvent être invoqués en les abordant (avec des adresses 16 bits).
alors utiliser unicode en pdf directement
- vous auriez à convertir les polices normales en CID-Fonts, ce qui est probablement extrêmement difficile - vous auriez à générer les routines graphiques à partir de la police originale(?), extrait de caractères métriques, etc.
- vous ne pouvez pas utiliser les polices CID comme les polices normales-vous ne pouvez pas les charger ou les mettre à l'échelle comme vous chargez et mettez à l'échelle les polices normales
- de plus, les caractères de 2 octets ne couvrent même pas tout L'Espace Unicode
à mon humble avis, ces les points rendent absolument impossible l'utilisation d'unicode directement.
ce que je fais maintenant c'est d'utiliser les caractères indirectement de la façon suivante: Pour chaque police, je génère une page de code (et une table de recherche pour des recherches rapides)- en c++ ce serait quelque chose comme
std::map<std::string, std::vector<wchar_t> > Codepage;
std::map<std::string, std::map<wchar_t, int> > LookupTable;
ensuite, chaque fois que je veux mettre une chaîne unicode sur une page, je itère ses caractères, les rechercher dans la table de recherche et - si elles sont neuves, je les ajouter à la page de code comme ceci:
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);
}
}
puis, je génère une nouvelle chaîne, où les caractères de la chaîne originale sont remplacés par leurs positions dans la page de code comme ceci:
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 += ">";
par exemple, "H€llo Monde!"pourrait devenir < 01020303040506040703080905> et maintenant vous pouvez simplement mettre cette chaîne dans le pdf et la faire imprimer, en utilisant l'opérateur Tj comme d'habitude...
mais vous avez maintenant un problème: le pdf ne sait pas que tu veux dire "H" par 01. Pour résoudre ce problème, vous devez également inclure le codepage dans le fichier pdf. Ceci est fait en ajoutant un /Encodage à L'objet Font et à sonDifférences
pour le " H€llo World!"par exemple, cette Police-Objet devrait fonctionner:
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
je la générer avec ce code:
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";
notez que j'utilise une police globale-register - j'utilise les mêmes noms de police /F1, /F2,... tout au long du pdf document. La même police-registre objet est référencé dans l' /Ressources Entrée de toutes les pages. Si vous le faites différemment (par exemple, vous utilisez une police-registre par page), vous devrez peut - être adapter le code à votre situation...
Alors, comment trouvez-vous les noms des glyphes (/Euro pour "€", /exclam pour "!" etc.)? Dans le code ci-dessus, ceci est fait en appelant simplement "GlyphName(*j)". J'ai généré cette méthode avec un Script BASH de la liste trouvée à
http://www.jdawiseman.com/papers/trivia/character-entities.html
et il ressemble à ceci
const std::string GlyphName(wchar_t UnicodeCodepoint)
{
switch(UnicodeCodepoint)
{
case 0x00A0: return "nonbreakingspace";
case 0x00A1: return "exclamdown";
case 0x00A2: return "cent";
...
}
}
problème majeur j'ai laissé ouvert, c'est que ce ne fonctionne que si vous utilisez au maximum 254 caractères différents à partir de la même police. Pour utiliser plus de 254 caractères différents, vous devez créer plusieurs codepages pour la même police.
à l'intérieur du pdf, différentes codepages sont représenté par des polices différentes, donc pour passer d'une page à l'autre, il faudrait changer de police, ce qui pourrait théoriquement faire exploser votre pdf un peu, mais moi pour ma part, je peux vivre avec ça...
Je ne suis pas un expert PDF, et (comme L'a dit Ferruccio) les spécifications PDF à Adobe devrait tout vous dire, mais une pensée a surgi dans mon esprit:
êtes-vous sûr d'utiliser une police qui supporte tous les caractères dont vous avez besoin?
dans notre application, nous créons PDF à partir de pages HTML (avec une bibliothèque tierce), et nous avons eu ce problème avec les caractères cyrilliques...