PDFlib Cookbook

cookbook

fonts/opentype_features_for_cjk

Demonstrate the use of OpenType features for CJK fonts and text.

Download Java Code  Switch to PHP Code  Show Output 

/*
 * Demonstrate OpenType font features for Chinese, Japanese, and Korean text
 *
 * Demonstrate various CJK OpenType features after checking
 * whether a particular feature is supported in a font.
 *
 * Required software: PDFlib/PDFlib+PDI/PPS 9
 * Required data: CJK font with OpenType CJK features
 * 
 * For better results you should replace the default font with a suitable
 * commercial font. Depending on the implementation of the features in
 * the font you may have to replace the sample text below in order to
 * see some effect of the features.
 * 
 * Note that even if a particular OpenType feature is present in the font
 * it may not necessarily affect the characters provided in the test strings
 * below. In this case you must replace the test strings with content which
 * is suited to your font.
 */

package com.pdflib.cookbook.pdflib.fonts;

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

class opentype_features_for_cjk {
    public static void main(String argv[]) {
        /* This is where the data files are. Adjust as necessary. */
        final String searchpath = "../input";
        
        final String outfile = "opentype_features_for_cjk.pdf";

        String optlist;
        pdflib p = null;
        int i, table, font;
        final double llx = 25, lly = 20, urx = 825, ury = 580;
        String result;
        int exitcode = 0;

        final String testfont = "NotoSerifCJKjp-Regular";

        final String headers[] = { "Description", "Option list",
            "Font name", "Raw input (feature disabled)", "Feature enabled" };

        class testcase {
            testcase(String description, String optlist,
                            String feature, String text) {
                this.description = description;
                this.optlist = optlist;
                this.feature = feature;
                this.text = text;
            }

            String description;
            String optlist;
            String feature;
            String text;
        }

        final testcase testcases[] = {
            new testcase("Localized forms for simplified Chinese",
                        "features={locl} script=hang language=ZHS", "locl",
                        "\u2E95\u2EDD\u38C0"),

            new testcase("Localized forms for traditional Chinese",
                        "features={locl} script=hang language=ZHT", "locl",
                        "\u328B\u35EB\u3B35"),

            new testcase("Localized forms for Korean",
                        "features={locl} script=hang language=KOR", "locl",
                        "\u4E73\u52E4\u52FB"),

            new testcase("Simplified forms",
                        "features={smpl}", "smpl",
                        "\u6AAF \u81FA \u98B1"),

            new testcase("Traditional forms",
                        "features={trad}", "trad",
                        "\u53F0\u4E03\u4E0E\u4E31\u7D1B\u9912\u96B8" +
                        "\u9698\u966A\u939A"),

            new testcase("Expert forms",
                        "features={expt}", "expt",
                        "\u5516\u82A6\u98F4\u9E78\u9D60\u6F23\u84EE\u7BDD"),

            new testcase("Full widths",
                        "features={fwid}", "fwid",
                        "0123-456(789)=25"),

            new testcase("Horizontal kana alternates",
                        "features={hkna}", "hkna",
                        "\u3041\u3042\u3043\u3044 \u30A1\u30A2\u30A3" +
                        "\u30A4\u30E5\u30E4\u30B4\u30A7"),

            new testcase("Hangul",
                        "features={hngl}", "hngl",
                        "\u4F3D"),

            new testcase("Hojo kanji forms (JIS X 0212-1990)",
                        "features={hojo}", "hojo",
                        "\u6FF9\u9B35\u8200\u9CE6\u9B2D\u9721\u884B"),

            new testcase("Half widths",
                        "features={hwid}", "hwid",
                        "0123-456(789)=25"),

            new testcase("Italics",
                        "features={ital}", "ital",
                        "ABCD abcd 1234"),

            new testcase("JIS2004 forms",
                        "features={jp04}", "jp04",
                        "\u9022\u98F4\u6EA2\u8328\u9C2F\u6DEB\u8FC2"),

            new testcase("JIS78 forms",
                        "features={jp78}", "jp78",
                        "\u5516\u53F1\u63B4\u8000"),

            new testcase("JIS83 forms",
                        "features={jp83}", "jp83",
                        "\u4E08\u51A4"),

            new testcase("JIS90 forms",
                        "features={jp90}", "jp90",
                        "\u5026\u6994"),

            new testcase("Alternate annotation forms",
                        "features={nalt}", "nalt",
                        "ABC123\u002F\u30B4\u217B\u4E0B\u7981\u2F29\u4E2D\u2F42"),

            new testcase("NLC kanji forms",
                        "features={nlck}", "nlck",
                        "\u4FA0\u64B9"),

            new testcase("Proportional kana",
                        "features={pkna}", "pkna",
                        "\u3041\u3042 \u30A1\u30A2 \uFF66 \uFF67"),

            new testcase("Proportional widths",
                        "features={pwid}", "pwid",
                        "\u3041\u3042 \u30A1\u30A2 \u301C\uFF21\uFF23\uFF28" +
                        "\uFF29\uFF2D ABCD abcd 1234"),

            new testcase("Quarter widths",
                        "features={qwid}", "qwid",
                        "0123-456(789)=25"),

            new testcase("Ruby notation forms",
                        "features={ruby}", "ruby",
                        "\u3042\u304A\u307D\u30AA\u30BC\u30F1\u25C9\u31F0\u31FF"),

            new testcase("Traditional name forms",
                        "features={tnam}", "tnam",
                        "\u4E9C"),

            new testcase("Third widths",
                        "features={twid}", "twid",
                        "0123-456(789)=25"),
      
            new testcase("Vertical kana alternates",
                        "features={vkna} vertical", "vkna",
                        "\u3041\u3042\u3043\u3044\u31FF\u31F8\u30CB\u30BB"),

            new testcase("Vertical writing (option 'vertical' activates feature 'vert')",
                        "vertical", "vert",
                        "(a.b)\uFF1A-12/"),

            new testcase("Vertical alternates and rotation",
                        "features={vrt2} vertical", "vrt2",
                        "(a.b)\uFF1A-12/"),
        };

        try {
            p = new pdflib();

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

            /*
             * 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");

            /* 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", "opentype_features_for_cjk");

            table = -1;

            /* Table header */
            for (i = 0; i < headers.length; i++) {
                final int col = i + 1;

                optlist =
                    "fittextline={fontname=NotoSerif-Bold encoding=unicode fontsize=12} "
                    + "margin=4";
                table = p.add_table_cell(table, col, 1, headers[i], optlist);
            }

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

                int col = 1;

                /* Common option list for columns 1-3 */
                optlist =
                    "fittextline={fontname=NotoSerif-Regular encoding=unicode fontsize=12} "
                    + "margin=4";

                /* Column 1: feature description */
                table = p.add_table_cell(table, col++, row,
                        testcase.description, optlist);

                /* Column 2: option list */
                table = p.add_table_cell(table, col++, row, testcase.optlist,
                                optlist);

                /* Column 3: font name */
                table = p.add_table_cell(table, col++, row, testfont, optlist);

                /* Column 4: raw input text with feature disabled */
                optlist = "fittextline={fontname={" + testfont
                        + "} encoding=unicode fontsize=12 embedding} margin=4";
                table = p.add_table_cell(table, col++, row, testcase.text,
                        optlist);

                /*
                 * Column 5: text with enabled feature, or warning if the
                 * feature is not available in the font
                 */
                font = p.load_font(testfont, "unicode", "embedding");

                /* Check whether font contains the required feature table */
                optlist = "name=" + testcase.feature;
                if (p.info_font(font, "feature", optlist) == 1) {
                    /* feature is available: apply it to the text */
                    optlist = "margin=4 fittextline={fontname={" + testfont
                            + "} encoding=unicode fontsize=12 embedding "
                            + testcase.optlist + "}";
                    table = p.add_table_cell(table, col++, row, testcase.text,
                            optlist);
                }
                else {
                    /* feature is not available: emit a warning */
                    optlist = "fittextline={fontname=NotoSerif-Regular encoding=unicode "
                            + "fontsize=12 fillcolor=red} margin=4";
                    table = p.add_table_cell(table, col++, row,
                            "(feature not available in this font)", 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.println("PDFlib exception occurred:");
            System.err.println("[" + e.get_errnum() + "] " + e.get_apiname() +
                ": " + e.get_errmsg());
            exitcode = 1;
        }
        catch (Exception e) {
            System.err.println(e);
            exitcode = 1;
        }
        finally {
            if (p != null) {
                p.delete();
            }
            System.exit(exitcode);
        }
    }
}