PDFlib Cookbook

cookbook

fonts/glyph_availability

Download Java Code     Switch to PHP Code     Show Output PDF

/* $Id: glyph_availability.java,v 1.14 2017/01/26 13:13:35 stm Exp $
 * 
 * Glyph availability: Check the availability of glyphs in a font
 *
 * Load a font with "encoding=unicode". Then, for a specific Unicode character, 
 * output a row in a table containing the following information:
 *
 * 1) Font name
 * 2) Unicode codepoint and character name
 * 3) Glyph if available
 * 4) Glyph name if available
 * 
 * Required software: PDFlib/PDFlib+PDI/PPS 9
 * Required data: Font files. The sample looks for the font file "arialuni.ttf"
 * ("Arial Unicode MS"). Put this file into the "extra_input" directory.
 * 
 * This example is not run by default from the Ant build.xml file and can be
 * executed either specifically by invoking "ant glyph_availability" or together
 * with the other topics that require extra configuration by invoking "ant
 * run-extra-topics". 
 */

package com.pdflib.cookbook.pdflib.fonts;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.pdflib.pdflib;
import com.pdflib.PDFlibException;

class glyph_availability {

    static private class testcase {
        testcase(String font_name, String font_optlist,
            String character, String character_desc, boolean charref) {
            this.font_name = font_name;
            this.font_optlist = font_optlist;
            this.character = character;
            this.character_desc = character_desc;
            this.charref = charref;
        }

        String font_name;
        String font_optlist;
        String character;
        String character_desc;
        boolean charref;
    }

    public static void main(String argv[]) {
        /* This is where the data files are. Adjust as necessary. */
        final String searchpath = "../input";
        /* Put extra fonts into the "extra_input" directory */
        final String extra_searchpath = "../extra_input";

        String outfile = "glyph_availability.pdf";
        String title = "Glyph Availability";

        String optlist;
        pdflib p = null;
        int i, table;
        final double llx = 50, lly = 50, urx = 800, ury = 550;
        String result;
	int exitcode = 0;

        final String headers[] = { "Font name", "Unicode character", 
                                "Glyph", "Glyph name" };

        final testcase testcases[] = {
            new testcase("Helvetica", "", "a", "LATIN LETTER A", false),
            new testcase("ScheherazadeRegOT", "", "\u0646", "ARABIC LETTER NOON", false),
            new testcase("arialuni", "", "\u0646", "ARABIC LETTER NOON", false),
            /* same character as above, but as PDFlib character reference */
            new testcase("Helvetica", "", "ن", "ARABIC LETTER NOON", true),
            new testcase("Helvetica", "", "\u2D33", "TIFINAGH LETTER YAG", false),
            new testcase("GenI102", "",  "\u017A", "LATIN SMALL LETTER Z WITH ACUTE", false),
            new testcase("Norasi", "", "\u017A", "LATIN SMALL LETTER Z WITH ACUTE", false),
            new testcase("Norasi", "", "\u20AC", "EURO SIGN", false),
            
            /*
             * Demonstration of Unicode character beyond U+FFFF. With a PDFlib
             * character reference this is straightforward. With Java characters
             * this must be expressed with a UTF-16 surrogate pair. The Unicode
             * PUA character U+F0001 is checked in both ways in the
             * "fallback.ttf" font.
             */
            new testcase("fallback", "", "󰀁",
                "PUA CHARACTER U+F0001 (character reference)", true),
            new testcase("fallback", "", "\uDB80\uDC01",
                "PUA CHARACTER U+F0001 (surrogate pair)", false),
        };

        try {
            p = new pdflib();

            p.set_option("searchpath={" + searchpath + "}");
            p.set_option("searchpath={" + extra_searchpath + "}");

            /*
             * This means that formatting and other errors will raise an
             * exception. This simplifies our sample code, but is not
             * recommended for production code.
             */
            p.set_option("errorpolicy=exception");
            
            /* The test cases can contain character references */
            p.set_option("charref=true");

            /* Set an output path according to the name of the topic */
            if (p.begin_document(outfile, "") == -1) {
                throw new Exception("Error: " + p.get_errmsg());
            }

            p.set_info("Creator", "PDFlib Cookbook");
            p.set_info("Title", title + " $Revision: 1.14 $");

            table = -1;

            /* Table header */
            optlist = "fittextline={fontname=Helvetica-Bold "
                + "encoding=unicode fontsize=12} margin=4";
            for (i = 0; i < headers.length; i++) {
                table = p.add_table_cell(table, i + 1, 1, headers[i], optlist);
            }

            /* Create a table with feature samples, one feature per table row */
            for (i = 0; i < testcases.length; i++) {
                final testcase testcase = testcases[i];
                final int row = i + 2;

                /*
                 * Try to load the fonts, output a row that shows the missing
                 * font if a font can't be loaded.
                 */
                final String error_optlist = "fittextline={fontname=Helvetica " 
                    + "encoding=unicode fontsize=12 fillcolor=red} "
                    + "margin=4";

                final String font_optlist = testcase.font_optlist
                                + " errorpolicy=return";

                final int font = p.load_font(testcase.font_name, "unicode",
                    font_optlist);
                if (font != -1) {
                    table = put_row(p, table, row, font, testcase);
                }
                else {
                    table = p.add_table_cell(table, 1, row,
                        testcase.font_name + ": font not available",
                        error_optlist);
                }
            }

            /*
             * Loop until all of the table is placed; create new pages as long
             * as more table instances need to be placed.
             */
            do {
                p.begin_page_ext(0, 0, "width=a4.height height=a4.width");

                optlist = "header=1 fill={{area=rowodd fillcolor={gray 0.9}}} "
                    + "stroke={{line=other}} ";

                /* Place the table instance */
                result = p.fit_table(table, llx, lly, urx, ury, optlist);

                if (result.equals("_error"))
                    throw new Exception("Couldn't place table: "
                        + p.get_errmsg());

                p.end_page_ext("");
            }
            while (result.equals("_boxfull"));

            p.end_document("");
        }
        catch (PDFlibException e) {
            System.err.print("PDFlib exception occurred:\n");
            System.err.print("[" + e.get_errnum() + "] " + e.get_apiname()
                + ": " + e.get_errmsg() + "\n");
	    exitcode = 1;
        }
        catch (Exception e) {
            System.err.println(e.getMessage());
	    exitcode = 1;
        }
        finally {
            if (p != null) {
                p.delete();
            }
	    System.exit(exitcode);
        }
    }

    /**
     * Output one row with information regarding one specific character
     * 
     * @param p
     *            the pdflib object
     * @param table
     *            the table handle
     * @param row
     *            the number of the current row
     * @param font
     *            the font handle for the original font
     * @param t
     *            the current test case
     *            
     * @return the table handle
     * 
     * @throws PDFlibException
     * @throws Exception
     */
    static int put_row(pdflib p, int table, int row, int font, testcase t)
        throws PDFlibException, Exception {
        int col = 1;

        /*
         * Common option list for all columns except the "Actual glyph" column
         */
        final String common_optlist = "fittextline={fontname=Helvetica " 
            + "encoding=unicode fontsize=12} margin=4";

        /*
         * Column 1: Font name
         */
        table = p.add_table_cell(table, col++, row, t.font_name, common_optlist);

        /*
         * Column 2: Unicode value and name of Unicode character
         */
        final String uv = get_unichar(t.character, t.charref);
        final String char_desc = uv + " (" + t.character_desc + ")";
        table = p.add_table_cell(table, col++, row, char_desc, common_optlist);

        /*
         * Determine whether a glyph is available, and if so, determine
         * the glyph name, if available.
         */
        final int gid = (int) p.info_font(font, "glyphid", "unicode=" + uv);

        String display_character;
        String gn;
        if (gid != -1) {
            display_character = t.character;

            final int gn_idx = (int) p.info_font(font, "glyphname",
                                                    "unicode=" + uv);
            if (gn_idx != -1) {
                gn = p.get_string(gn_idx, "");
            }
            else {
                gn = "n/a";
            }
        }
        else {
            display_character = "n/a";
            gn = "n/a";
        }

        /*
         * Column 3: Actual glyph, if available.
         */
        final String testfont_optlist = "fittextline={font=" + font
                + " fontsize=12} margin=4";
        table = p.add_table_cell(table, col++, row, display_character,
                testfont_optlist);

        /*
         * Column 4: Glyph name
         */
        table = p.add_table_cell(table, col++, row, gn, common_optlist);

        return table;
    }

    /**
     * Get a PDFlib "Unichar" string (see PDFlib Tutorial, "1.1.1 Syntax",
     * "Unichar").
     * 
     * @param character
     *            A String containing logically a single character.
     * @param charref
     *            If true, the character parameter contains a PDFlib character
     *            reference, otherwise it contains the UTF-16 representation of
     *            a single Unicode code point (which can either be a single char
     *            or a surrogate pair for Unicode code points beyond U+FFFF).
     * 
     * @return the character in PDFlib "Unichar" notation
     * 
     * @throws Exception Incorrect test case supplied
     */
    static String get_unichar(String character, boolean charref)
            throws Exception {
        String result;
        
        if (charref)
        {
            /* Extract Unicode code point value from character reference */
            Pattern p = Pattern.compile("^&#x([A-Fa-f\\d]{4,5});$",
                                        Pattern.CASE_INSENSITIVE);
            Matcher m = p.matcher(character);
            if (!m.matches()) {
                throw new Exception("Incorrect character reference '"
                    + character + "' in test case with charref=true");
            }
            
            result = "U+" + m.group(1);
        }
        else
        {
            /* Determine the number of Unicode code points, which must be 1 */
            int codePointCount =
                Character.codePointCount(character, 0, character.length());
            
            if (codePointCount != 1) {
                throw new Exception("The number of Unicode code points in a " +
                		"test case without charref=true must be 1");
            }
            
            /*
             * Get the Unicode code point corresponding to the sequence of
             * Java chars in the string.
             */
            int codePoint = Character.codePointAt(character, 0);
            
            result = String.format("U+%04X", codePoint);
        }
        
        return result;
    }
}