Poignée beaucoup caracters unicode avec PDFBox

Cccompany:

Je suis en train d' écrire une fonction Java qui prend une chaîne en tant que paramètre et produire un document PDF en sortie avec PDFBox .

Tout fonctionne très bien tant que j'utilise des caractères latins. Cependant, je ne sais pas à l' avance ce que sera l'entrée , et il pourrait être un peu l' anglais, ainsi que les caractères chinois ou japonais.

Dans le cas de caractères non latins, voici l'erreur que je reçois:

Exception in thread "main" java.lang.IllegalArgumentException: U+3053 ('kohiragana') is not available in this font Helvetica encoding: WinAnsiEncoding
at org.apache.pdfbox.pdmodel.font.PDType1Font.encode(PDType1Font.java:426)
at org.apache.pdfbox.pdmodel.font.PDFont.encode(PDFont.java:324)
at org.apache.pdfbox.pdmodel.PDPageContentStream.showTextInternal(PDPageContentStream.java:509)
at org.apache.pdfbox.pdmodel.PDPageContentStream.showText(PDPageContentStream.java:471)
at com.mylib.pdf.PDFBuilder.generatePdfFromString(PDFBuilder.java:122)
at com.mylib.pdf.PDFBuilder.main(PDFBuilder.java:111)

Si je comprends bien, je dois utiliser une police spécifique pour le japonais, l'autre pour le chinois et ainsi de suite, parce que celui que j'utilise (Helvetiva) ne gère pas tous les caractères unicode requis.

Je pourrais aussi utiliser une police qui gère tous ces caractères unicode, comme Arial Unicode . Cependant , cette police est sous une licence spécifique , donc je ne peux pas l' utiliser et je ne l' ai pas trouvé un autre.

J'ai trouvé quelques projets qui veulent surmonter ce problème, comme le projet Google NOTO . Cependant, ce projet fournit des fichiers de polices multiples . Je dois donc choisir, lors de l' exécution, le fichier correct à charger en fonction de l'entrée que j'ai.

Je suis donc en face de 2 options, l'une dont je ne sais pas comment mettre en œuvre correctement:

  1. Continuez à chercher une police qui poignée presque tous les caractères unicode (où est-ce graal je cherche désespérément ?!)

  2. Essayez de détecter la langue utilisée et sélectionnez une police en dépendent. Malgré le fait que je ne sais pas (encore) comment faire, je ne trouve pas que ce soit une mise en œuvre propre, comme la correspondance entre l'entrée et le fichier de police sera codé en dur, ce qui signifie que je vais devoir coder en dur tous les correspondances possibles.

  3. Y at-il une autre solution?

  4. Suis-je complètement les pistes?

Merci d'avance pour votre aide et des conseils!

Voici le code que j'utilise pour générer le PDF:

public static void main(String args[]) throws IOException {
    String latinText = "This is latin text";
    String japaneseText = "これは日本語です";

    // This works good
    generatePdfFromString(latinText);

    // This generate an error
    generatePdfFromString(japaneseText);
}

private static OutputStream generatePdfFromString(String content) throws IOException {
    PDPage page = new PDPage();

    try (PDDocument doc = new PDDocument();
         PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
        doc.addPage(page);
        contentStream.setFont(PDType1Font.HELVETICA, 12);

        // Or load a specific font from a file
        // contentStream.setFont(PDType0Font.load(this.doc, new File("/fontPath.ttf")), 12);

        contentStream.beginText();
        contentStream.showText(content);
        contentStream.endText();
        contentStream.close();
        OutputStream os = new ByteArrayOutputStream();
        doc.save(os);
        return os;
    }
}
MKL:

Une meilleure solution que d'attendre une police ou de deviner la langue d'un texte est d'avoir une multitude de polices et de sélectionner la bonne police sur une base glyphe par glyphe.

Vous avez déjà trouvé le Google Fonts Noto qui sont une bonne collection de base de polices pour cette tâche.

Malheureusement, cependant, Google publie les polices Noto CJC uniquement les polices OpenType (.otf), et non comme des polices TrueType (.ttf), une politique qui ne risque pas de changement, cf. les polices Noto émettent 249 et d' autres. D'autre part PDFBox ne prend pas en charge les polices OpenType et ne fonctionne pas activement sur le soutien OpenType soit, cf. PDFBox-2482 .

Ainsi, on doit convertir la police OpenType en quelque sorte à TrueType. J'ai simplement pris le fichier partagé par djmilch dans son blog post GRATUIT FONT NOTO SANS CJK EN TTF .

Sélection de la police par caractère

Vous devez donc essentiellement une méthode qui vérifie votre texte caractère par caractère et dissèque en morceaux qui peuvent être tirées en utilisant la même police.

Malheureusement , je ne vois pas une meilleure méthode pour demander un PDFBox PDFontsi elle connaît un glyphe pour un caractère donné que de réellement essayer de coder le caractère et d' envisager un IllegalArgumentException« non ».

Je suis donc mis en œuvre cette fonctionnalité en utilisant la classe d'aide suivante TextWithFontet méthode fontify:

class TextWithFont {
    final String text;
    final PDFont font;

    TextWithFont(String text, PDFont font) {
        this.text = text;
        this.font = font;
    }

    public void show(PDPageContentStream canvas, float fontSize) throws IOException {
        canvas.setFont(font, fontSize);
        canvas.showText(text);
    }
}

( AddTextWithDynamicFonts de classe interne)

List<TextWithFont> fontify(List<PDFont> fonts, String text) throws IOException {
    List<TextWithFont> result = new ArrayList<>();
    if (text.length() > 0) {
        PDFont currentFont = null;
        int start = 0;
        for (int i = 0; i < text.length(); ) {
            int codePoint = text.codePointAt(i);
            int codeChars = Character.charCount(codePoint);
            String codePointString = text.substring(i, i + codeChars);
            boolean canEncode = false;
            for (PDFont font : fonts) {
                try {
                    font.encode(codePointString);
                    canEncode = true;
                    if (font != currentFont) {
                        if (currentFont != null) {
                            result.add(new TextWithFont(text.substring(start, i), currentFont));
                        }
                        currentFont = font;
                        start = i;
                    }
                    break;
                } catch (Exception ioe) {
                    // font cannot encode codepoint
                }
            }
            if (!canEncode) {
                throw new IOException("Cannot encode '" + codePointString + "'.");
            }
            i += codeChars;
        }
        result.add(new TextWithFont(text.substring(start, text.length()), currentFont));
    }
    return result;
}

( AddTextWithDynamicFonts méthode)

exemple d'utilisation

En utilisant la méthode et la classe ci-dessus comme celui-ci

String latinText = "This is latin text";
String japaneseText = "これは日本語です";
String mixedText = "Tこhれiはs日 本i語sで すlatin text";

generatePdfFromStringImproved(latinText).writeTo(new FileOutputStream("Cccompany-Latin-Improved.pdf"));
generatePdfFromStringImproved(japaneseText).writeTo(new FileOutputStream("Cccompany-Japanese-Improved.pdf"));
generatePdfFromStringImproved(mixedText).writeTo(new FileOutputStream("Cccompany-Mixed-Improved.pdf"));

( AddTextWithDynamicFonts essai testAddLikeCccompanyImproved)

ByteArrayOutputStream generatePdfFromStringImproved(String content) throws IOException {
    try (   PDDocument doc = new PDDocument();
            InputStream notoSansRegularResource = AddTextWithDynamicFonts.class.getResourceAsStream("NotoSans-Regular.ttf");
            InputStream notoSansCjkRegularResource = AddTextWithDynamicFonts.class.getResourceAsStream("NotoSansCJKtc-Regular.ttf")   ) {
        PDType0Font notoSansRegular = PDType0Font.load(doc, notoSansRegularResource);
        PDType0Font notoSansCjkRegular = PDType0Font.load(doc, notoSansCjkRegularResource);
        List<PDFont> fonts = Arrays.asList(notoSansRegular, notoSansCjkRegular);

        List<TextWithFont> fontifiedContent = fontify(fonts, content);

        PDPage page = new PDPage();
        doc.addPage(page);
        try (   PDPageContentStream contentStream = new PDPageContentStream(doc, page)) {
            contentStream.beginText();
            for (TextWithFont textWithFont : fontifiedContent) {
                textWithFont.show(contentStream, 12);
            }
            contentStream.endText();
        }
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        doc.save(os);
        return os;
    }
}

( AddTextWithDynamicFonts méthode helper)

Je reçois

  • pour latinText = "This is latin text"

    capture d'écran latin

  • pour japaneseText = "これは日本語です"

    capture d'écran japonais

  • et pour mixedText = "Tこhれiはs日 本i語sで すlatin text"

    capture d'écran mixte

certains apartés

  • Je récupérai les polices que les ressources Java , mais vous pouvez utiliser tout type de InputStreampour eux.

  • Le mécanisme de sélection des polices ci - dessus peuvent être facilement combinés avec le mécanisme de rupture de ligne indiqué dans cette réponse et l'extension de sa justification dans cette réponse

Je suppose que tu aimes

Origine http://43.154.161.224:23101/article/api/json?id=201397&siteId=1
conseillé
Classement