Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8265761

Font with missed font family name is not properly printed on Windows

    XMLWordPrintable

Details

    • 2d
    • b21
    • generic
    • windows

    Backports

      Description

        PDFBox 1.8 uses scaled glyphs to print a text [1] and PDFBox 2.0 draws a text by drawing its path [2].

        Recent fix to PDFBox allows to pass a custom PDFRenderer to PDFPrintable [3] so it is possible to override PDFBox PageDrawer.showGlyph(...) method and use Graphics2D.drawString(...) method to draw a text.

        Some tests showed that there is a pdf document which prints all but one font properly using Graphics2D.drawString(...) method on Windows. This pdf doc is properly printed by PDFBox 1.8 and 2.0.

        The issue is not reproduced on Linux. The pdf document in question is properly printed by PDFBox 1.8, 2.0, and PDFBox with custom Graphics2D.drawString(...) method on Ubuntu 20.04.

        Further investigations showed that the font which is not properly printed with Graphics2D.drawString(...) method has an empty font name.

        To reproduce the issue I created a simple test font which contains only capital letters "ABCDEF" and saved it with empty font family name (see attached fonts SampleBow.ttf and SampleBowMissedFamilyName.ttf).

        Run the code below with the provided font which has an empty font name:
        > java PrintFontSample SampleBowMissedFamilyName.ttf

        The sample prints the text with the provided font using 3 ways:
        - Graphics2D.drawString(...)
        - Graphics2D.drawGlyphVector(...)
        - Graphics2D.drawGlyphVector(...) using transformed glyphs

        The first and the second methods use GDI TextOut method to print a text and the third method draws a text as a Path in GDI.

        The attached sample-doc-without-fix.pdf doc shows how the sample is printed using jdk 16. The first and the second lines are printed with thin lines and only the third line is printed by the provided font.

        The reason is that an empty string is provided as a family font name to awt_PrintJob.jFontToWFontA(...) method [4] and the expected font is not selected in GDI.

        The proposed solution could be to return false when an empty family font name is passed to awt_PrintJob.jFontToWFontA(...) method so the text printing falls back to using GDI print Path method.


        [1] https://github.com/apache/pdfbox/blob/41ae21bd4c3f304373d3b05f63af5325df248019/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/font/PDSimpleFont.java#L352

        [2] https://github.com/apache/pdfbox/blob/4f14dee47ff821e44d9e2ff11532959d95e94d5b/pdfbox/src/main/java/org/apache/pdfbox/rendering/PageDrawer.java#L512

        [3] https://github.com/apache/pdfbox/commit/7d9f08516c6c9967351f426bbb9d4b6104257835#diff-fea229ecdbc8fbc77551966810d22ce23a3d993e2df3d70c281eefc44fdf65f6

        [4] https://github.com/openjdk/jdk/blob/e16d568c1f5d7030b9e038e21fb3815ae5b1163a/src/java.desktop/windows/native/libawt/windows/awt_PrintJob.cpp#L2264

        --- PrintFontSample.java ---

        import javax.print.PrintServiceLookup;
        import javax.swing.*;
        import java.awt.*;
        import java.awt.font.FontRenderContext;
        import java.awt.font.GlyphVector;
        import java.awt.geom.AffineTransform;
        import java.awt.print.PageFormat;
        import java.awt.print.Printable;
        import java.awt.print.PrinterJob;
        import java.io.File;

        public class PrintFontSample {

            private static final String TEXT = "ABCDEF";
            private static final float FONT_SIZE = 43;

            public static void main(String[] args) throws Exception {

                if (args.length < 1) {
                    System.err.printf("Provide path to the font file:%n");
                    System.err.printf(" > PrintFontSample SampleBowMissedFamilyName.ttf%n");
                    System.exit(1);
                }

                File fontFile = new File(args[0]);
                if (!fontFile.exists()) {
                    System.err.printf("Provided font file does not exist: %s%n", args[0]);
                    System.exit(1);
                }

                final Font font = Font
                        .createFont(Font.TRUETYPE_FONT, fontFile)
                        .deriveFont(FONT_SIZE);

                SwingUtilities.invokeAndWait(() -> {
                    try {
                        Printable printable = new PrintSample(font);
                        PrinterJob job = PrinterJob.getPrinterJob();
                        job.setPrintService(PrintServiceLookup.lookupDefaultPrintService());
                        job.setPrintable(printable);
                        job.print();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
            }

            static class PrintSample implements Printable {

                private final Font font;

                public PrintSample(Font font) {
                    this.font = font;
                }

                @Override
                public int print(Graphics graphics, PageFormat pageFormat, int index) {
                    if (index == 0) {
                        drawText((Graphics2D) graphics, font, TEXT, 100, 150);
                        return PAGE_EXISTS;
                    } else {
                        return NO_SUCH_PAGE;
                    }
                }
            }

            private static void drawText(Graphics2D g, Font font, String text, int x, int y) {

                int dy = (int) FONT_SIZE + 5;

                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                int fontSize = font.getSize();

                // draw string
                g.setFont(font);
                g.drawString(text, x, y);

                // draw GlyphVector
                FontRenderContext frc = new FontRenderContext(new AffineTransform(), true, true);
                GlyphVector glyphs = font.createGlyphVector(frc, text);
                g.drawGlyphVector(glyphs, x, y + dy);

                // draw scaled GlyphVector
                glyphs = font.deriveFont(1.0f).createGlyphVector(frc, text);

                AffineTransform scale = AffineTransform.getScaleInstance(fontSize, fontSize);
                for (int i = 0; i < glyphs.getNumGlyphs(); i++) {
                    glyphs.setGlyphTransform(i, scale);
                }

                g.drawGlyphVector(glyphs, x, y + 2 * dy);
            }
        }
        --- --- ---

        Attachments

          1. PrintFontSample.java
            3 kB
          2. PrintFontSample-1.java
            3 kB
          3. SampleBow.ttf
            3 kB
          4. SampleBowMissedFamilyName.ttf
            3 kB
          5. SampleBowOneSpaceFamilyName.ttf
            3 kB
          6. SampleBowTwoSpacesFamilyName.ttf
            3 kB
          7. sample-doc-with-fix.pdf
            3 kB
          8. sample-doc-without-fix.pdf
            3 kB

          Issue Links

            Activity

              People

                alexsch Alexandr Scherbatiy
                alexsch Alexandr Scherbatiy
                Votes:
                0 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: