/* * Copyright 2015 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.pdfbox.debugger.fontencodingpane; import java.awt.geom.AffineTransform; import java.awt.geom.NoninvertibleTransformException; import java.awt.image.BufferedImage; import org.apache.pdfbox.pdmodel.font.PDSimpleFont; import org.apache.pdfbox.pdmodel.font.PDType3Font; import javax.swing.JPanel; import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPageContentStream; import org.apache.pdfbox.pdmodel.PDResources; import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.font.PDType3CharProc; import org.apache.pdfbox.rendering.PDFRenderer; import org.apache.pdfbox.util.Charsets; import org.apache.pdfbox.util.Matrix; /** * @author Khyrul Bashar * @author Tilman Hausherr * * A class that shows the glyph table along with unicode characters for PDType3Font. */ class Type3Font extends FontPane { public static final String NO_GLYPH = "No glyph"; private final FontEncodingView view; private int totalAvailableGlyph = 0; private PDRectangle fontBBox; private final PDResources resources; /** * Constructor. * @param font PDSimpleFont instance. * @throws IOException If fails to parse unicode characters. */ Type3Font(PDType3Font font, PDResources resources) throws IOException { this.resources = resources; calcBBox(font); Object[][] tableData = getGlyphs(font); Map<String, String> attributes = new LinkedHashMap<>(); attributes.put("Font", font.getName()); attributes.put("Encoding", getEncodingName(font)); attributes.put("Glyphs", Integer.toString(totalAvailableGlyph)); view = new FontEncodingView(tableData, attributes, new String[] {"Code", "Glyph Name", "Unicode Character", "Glyph"}, null); } private void calcBBox(PDType3Font font) throws IOException { double minX = 0; double maxX = 0; double minY = 0; double maxY = 0; for (int index = 0; index <= 255; ++index) { PDType3CharProc charProc = font.getCharProc(index); if (charProc == null) { continue; } PDRectangle glyphBBox = charProc.getGlyphBBox(); if (glyphBBox == null) { continue; } minX = Math.min(minX, glyphBBox.getLowerLeftX()); maxX = Math.max(maxX, glyphBBox.getUpperRightX()); minY = Math.min(minY, glyphBBox.getLowerLeftY()); maxY = Math.max(maxY, glyphBBox.getUpperRightY()); } fontBBox = new PDRectangle((float) minX, (float) minY, (float) (maxX - minX), (float) (maxY - minY)); } private Object[][] getGlyphs(PDType3Font font) throws IOException { Object[][] glyphs = new Object[256][4]; for (int index = 0; index <= 255; index++) { glyphs[index][0] = index; if (font.getEncoding().contains(index)) { glyphs[index][1] = font.getEncoding().getName(index); glyphs[index][2] = font.toUnicode(index); if (fontBBox.toGeneralPath().getBounds2D().isEmpty()) { glyphs[index][3] = NO_GLYPH; } else { glyphs[index][3] = renderType3Glyph(font, index); } totalAvailableGlyph++; } else { glyphs[index][1] = NO_GLYPH; glyphs[index][2] = NO_GLYPH; glyphs[index][3] = NO_GLYPH; } } return glyphs; } // Kindof an overkill to create a PDF for one glyph, but there is no better way at this time. // Isn't called if no bounds are available private BufferedImage renderType3Glyph(PDType3Font font, int index) throws IOException { PDDocument doc = new PDDocument(); int scale = 1; if (fontBBox.getWidth() < 72 || fontBBox.getHeight() < 72) { // e.g. T4 font of PDFBOX-2959 scale = (int) (72 / Math.min(fontBBox.getWidth(), fontBBox.getHeight())); } PDPage page = new PDPage(new PDRectangle(fontBBox.getWidth() * scale, fontBBox.getHeight() * scale)); page.setResources(resources); try { try (PDPageContentStream cs = new PDPageContentStream(doc, page)) { cs.transform(Matrix.getTranslateInstance(-fontBBox.getLowerLeftX(), -fontBBox.getLowerLeftY())); try { AffineTransform at = font.getFontMatrix().createAffineTransform(); if (!at.isIdentity()) { at.invert(); cs.transform(new Matrix(at)); } } catch (NoninvertibleTransformException ex) { // "shouldn't happen" } cs.beginText(); cs.setFont(font, scale); //TODO support type3 font encoding in PDType3Font.encode cs.appendRawCommands(String.format("<%02X> Tj\n", index).getBytes(Charsets.ISO_8859_1)); cs.endText(); } doc.addPage(page); return new PDFRenderer(doc).renderImage(0); } finally { doc.close(); } } private String getEncodingName(PDSimpleFont font) { return font.getEncoding().getClass().getSimpleName(); } @Override public JPanel getPanel() { return view.getPanel(); } }