PDFlib Cookbook

cookbook

fonts/glyph_availability

Check the availability of glyphs in a font.

Download PHP Code  Switch to Java Code  Show Output 

<?php
/*
 *
 * Glyph availability:
 * Check the availability of glyphs in a font
 *
 * Load a font with "encoding=unicode" (default). 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 in the font
 * 4) Glyph name if available in the font
 *
 * Required software: PDFlib/PDFlib+PDI/PPS 10
 * Required data: Font files 
 */

/* This is where the data files are. Adjust as necessary. */
$searchpath = dirname(__FILE__,3)."/input";

$title = "Glyph Availability";

class testcase {
    function __construct($font_name, $font_optlist, $character, $character_desc) {
        $this->font_name = $font_name;
        $this->font_optlist = $font_optlist;
        $this->character = $character;
        $this->character_desc = $character_desc;
    }
}

$headers = array( "Font name", "Unicode character", "Glyph", "Glyph name");

$testcases = array(
    new testcase("NotoSerif-Regular", "", "a", "U+0061 LATIN LETTER A"),
            
    new testcase("NotoSerif-Regular", "", "\u{20AC}", "U+20AC EURO SIGN"),            
                
    new testcase("NotoSerif-Regular", "",  "\u{017A}", "U+017A LATIN SMALL LETTER Z WITH ACUTE"),

    new testcase("NotoSerif-Regular", "", "\u{2D33}", "U+2D33 TIFINAGH LETTER YAG"),

    new testcase("NotoNaskhArabic-Regular", "fallbackfonts={ {fontname=NotoSerif-Regular} }",
        "\u{0646}", "U+0646 ARABIC LETTER NOON"),
   
    new testcase("NotoNaskhArabic-Regular", "fallbackfonts={ {fontname=NotoSerif-Regular} }",
        "\u{017A}", "U+017A LATIN SMALL LETTER Z WITH ACUTE"),
    
    /*
     * Demonstration of Unicode characters beyond U+FFFF. The Unicode
     * character U+2000B (surrogate representation \uD840\DC0B) is
     * checked using a CJK font. The surrogates are resolved
     * by the Java compiler.
     * Languages which don't support surrogates or backslash syntax for
     * characters outside the BMP must use PDFlib character references
     * instead, e.g. &#x2000B;
     */

    new testcase("NotoSerifCJKjp-Regular", "", "\u{2000B}",
        "U+2000B CJK UNIFIED IDEOGRAPH"),
);

/*
 * Output one row with information regarding one specific character
 */
function put_row($p, $table, $row, $font, $t) {
    $col = 1;

    /*
     * Common option list for all columns except the "Actual glyph" column
     */
    $common_optlist = "fittextline={fontname=NotoSerif-Regular "
                        . "fontsize=12} margin=4";

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

    /*
     * Column 2: Unicode character
     */
    $table = $p->add_table_cell($table, $col++, $row, $t->character_desc, $common_optlist);
    /* The data type "Unichar" in option list expects character references
     * without the &...; decoration, while the decoration is required in
     * text output functions.
     * We don't use charrefs in the examples, but if you need them and
     * want to pass them e.g. to info_font() you can resolve charrefs
     * as follows:
     * 
     * $t_plain = $p->convert_to_unicode("utf8",
     *      $t->character, "outputformat=utf8 charref=true");
     * 
     * Conversion from UTF-8 to UTF-8 may look a bit silly, but we use
     * the convert_to_unicode() method to resolve character references.
     * The result can be used in both option lists and text output functions.
     * This is only required if character references are used in the text.
     */ 

    /*
     * Determine whether a glyph is available, and if so, determine
     * the glyph name, if available.
     */
    $gid = (int) $p->info_font($font, "glyphid", "unicode=" . $t->character);

    if ($gid != -1) {
        $display_character = $t->character;

        $gn_idx = (int) $p->info_font($font, "glyphname", "unicode=" . $t->character);
        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
    */
    $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;
}

try {
    $llx = 50; $lly = 50; $urx = 800; $ury = 550;
    
    $p = new PDFlib();

    $p->set_option("searchpath={" . $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");
    $p->set_option("charref=true");

    if ($p->begin_document("", "") == 0)
        throw new Exception("Error: " . $p->get_errmsg());

    $p->set_info("Creator", "PDFlib Cookbook");
    $p->set_info("Title", $title);
    
    $table = 0;

    /* Table header */
    $optlist = "fittextline={fontname=NotoSerif-Bold "
                . "fontsize=12} margin=4";
    for ($i = 0; $i < count($headers); $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 < count($testcases); $i++) {
        $testcase = $testcases[$i];
        $row = $i + 2;

        /*
         * Try to load the fonts, output a row that shows the missing
         * font if a font can't be loaded.
         */
        $error_optlist = "fittextline={fontname=NotoSerif-Regular " 
            . "fontsize=12 fillcolor=red} "
            . "margin=4";

        $font_optlist = $testcase->font_optlist . " errorpolicy=return";

        $font = $p->load_font($testcase->font_name, "unicode", $font_optlist);
        if ($font != 0) {
            $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 == "_error")
            throw new Exception("Couldn't place table: " . $p->get_errmsg());

        $p->end_page_ext("");
    }
    while ($result == "_boxfull");
    $p->end_document("");

    $buf = $p->get_buffer();
    $len = strlen($buf);

    header("Content-type: application/pdf");
    header("Content-Length: $len");
    header("Content-Disposition: inline; filename=glyph_availability.pdf");
    print $buf;

} catch (PDFlibException $e) {
    echo("PDFlib exception occurred:\n".
        "[" . $e->get_errnum() . "] " . $e->get_apiname() .
        ": " . $e->get_errmsg() . "\n");
    exit(1);
} catch (Throwable $e) {
    echo("PHP exception occurred: " . $e->getMessage() . "\n");
    exit(1);
}

$p = 0;

?>