PDFlib Cookbook

cookbook

complex_scripts/starter_shaping

Demonstrate text shaping for Arabic, Hebrew, Hindi and Sanskrit scripts. Right-to-left text is reordered according to the Bidi algorithm.

Download PHP Code  Switch to Java Code  Show Output 

<?php

/*
 * Starter sample for text shaping features
 * Demonstrate text shaping for Arabic, Hebrew and Devanagari scripts
 * Right-to-left text is reordered according to the Bidi algorithm.
 *
 * Required software: PDFlib/PDFlib+PDI/PPS 10
 * Required data: suitable fonts for the scripts
 */

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

$llx = 25; $lly = 50; $urx = 825; $ury = 550;

$header = array(
        "Language", "Raw input", "Reordered and shaped output"
);
$MAXCOL = count($header);

class shaping {
    public function __construct($fontopt, $optlist, $textflow, $language, $text) {
        $this->fontopt = $fontopt;
        $this->optlist = $optlist;
        $this->textflow = $textflow;
        $this->language = $language;
        $this->text = $text;
    }

    public $fontopt;         /* fontname and other font options */
    public $optlist;         /* text options */
    public $textflow;        /* can't use Textflow for Bidi text */
    public $language;        /* language name */
    public $text;            /* sample text */
}

$shapingsamples = array(
/* Dummys to compensate for header row and +/-1 indexing in C */
new shaping( "", "", 0, "", "" /* dummy1 */ ),
new shaping( "", "", 0, "", "" /* dummy2 */ ),

/* -------------------------- Arabic -------------------------- */
new shaping( "fontname=NotoNaskhArabic-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }", 
    "shaping script=arab", 0, "Arabic",
    "\u{0627}\u{0644}\u{0639}\u{064E}\u{0631}\u{064E}\u{0628}\u{0650}" .
    "\u{064A}\u{0629}" ),

new shaping( "fontname=NotoNaskhArabic-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=arab", 0, "Arabic",
    "\u{0645}\u{0631}\u{062D}\u{0628}\u{0627}! (Hello)"),

new shaping( 
    "fontname=NotoNaskhArabic-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=arab", 0, "Arabic",
    "\u{FEFF}\u{0627}\u{0644}\u{0645}\u{0627}\u{062F}\u{0629}\u{0020}" .
    "\u{0031}\u{0020}\u{064A}\u{0648}\u{0644}\u{062F}\u{0020}\u{062C}" .
    "\u{0645}\u{064A}\u{0639}\u{0020}\u{0627}\u{0644}\u{0646}\u{0627}" .
    "\u{0633}\u{0020}\u{0623}\u{062D}\u{0631}\u{0627}\u{0631}\u{064B}" .
    "\u{0627}\u{0020}\u{0645}\u{062A}\u{0633}\u{0627}\u{0648}\u{064A}" .
    "\u{0646}\u{0020}\u{0641}\u{064A}\u{0020}\u{0627}\u{0644}\u{0643}" .
    "\u{0631}\u{0627}\u{0645}\u{0629}\u{0020}\u{0648}\u{0627}\u{0644}" .
    "\u{062D}\u{0642}\u{0648}\u{0642}\u{002E}\u{0020}"),

new shaping( 
    "fontname=NotoNaskhArabic-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=arab", 0, "Arabic",
    "\u{0648}\u{0642}\u{062F}\u{0020}\u{0648}\u{0647}\u{0628}\u{0648}" .
    "\u{0627}\u{0020}\u{0639}\u{0642}\u{0644}\u{0627}\u{064B}\u{0020}" .
    "\u{0648}\u{0636}\u{0645}\u{064A}\u{0631}\u{064B}\u{0627}\u{0020}" .
    "\u{0648}\u{0639}\u{0644}\u{064A}\u{0647}\u{0645}\u{0020}\u{0623}" .
    "\u{0646}\u{0020}\u{064A}\u{0639}\u{0627}\u{0645}\u{0644}\u{0020}" .
    "\u{0628}\u{0639}\u{0636}\u{0647}\u{0645}\u{0020}\u{0628}\u{0639}" .
    "\u{0636}\u{064B}\u{0627}\u{0020}\u{0628}\u{0631}\u{0648}\u{062D}" .
    "\u{0020}\u{0627}\u{0644}\u{0625}\u{062E}\u{0627}\u{0621}\u{002E}"),

/* -------------------------- Hebrew -------------------------- */
new shaping( 
    "fontname=NotoSerifHebrew-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=hebr", 0, "Hebrew",
    "\u{05E2}\u{05B4}\u{05D1}\u{05B0}\u{05E8}\u{05B4}\u{05D9}\u{05EA}"),

new shaping( 
    "fontname=NotoSerifHebrew-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=hebr", 0, "Hebrew",
    "\u{05E1}\u{05E2}\u{05D9}\u{05E3}\u{0020}\u{05D0}\u{002E}\u{0020}" .
    "\u{05DB}\u{05DC}\u{0020}\u{05D1}\u{05E0}\u{05D9}\u{0020}\u{05D0}" .
    "\u{05D3}\u{05DD}\u{0020}\u{05E0}\u{05D5}\u{05DC}\u{05D3}\u{05D5}" .
    "\u{0020}\u{05D1}\u{05E0}\u{05D9}\u{0020}\u{05D7}\u{05D5}\u{05E8}" .
    "\u{05D9}\u{05DF}\u{0020}\u{05D5}\u{05E9}\u{05D5}\u{05D5}\u{05D9}" .
    "\u{05DD}\u{0020}\u{05D1}\u{05E2}\u{05E8}\u{05DB}\u{05DD}\u{0020}" .
    "\u{05D5}\u{05D1}\u{05D6}\u{05DB}\u{05D5}\u{05D9}\u{05D5}\u{05EA}" .
    "\u{05D9}\u{05D4}\u{05DD}\u{002E}\u{0020}"),

new shaping( 
    "fontname=NotoSerifHebrew-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=hebr", 0, "Hebrew",
    "\u{05DB}\u{05D5}\u{05DC}\u{05DD}\u{0020}\u{05D7}\u{05D5}\u{05E0}" .
    "\u{05E0}\u{05D5}\u{0020}\u{05D1}\u{05EA}\u{05D1}\u{05D5}\u{05E0}" .
    "\u{05D4}\u{0020}\u{05D5}\u{05D1}\u{05DE}\u{05E6}\u{05E4}\u{05D5}" .
    "\u{05DF}\u{002C}\u{0020}"),

new shaping( 
    "fontname=NotoSerifHebrew-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=hebr", 0, "Hebrew",
    "\u{05DC}\u{05E4}\u{05D9}\u{05DB}\u{05DA}\u{0020}\u{05D7}\u{05D5}" .
    "\u{05D1}\u{05D4}\u{0020}\u{05E2}\u{05DC}\u{05D9}\u{05D4}\u{05DD}" .
    "\u{0020}\u{05DC}\u{05E0}\u{05D4}\u{05D5}\u{05D2}\u{0020}\u{05D0}" .
    "\u{05D9}\u{05E9}\u{0020}\u{05D1}\u{05E8}\u{05E2}\u{05D4}\u{05D5}" .
    "\u{0020}\u{05D1}\u{05E8}\u{05D5}\u{05D7}\u{0020}\u{05E9}\u{05DC}" .
    "\u{0020}\u{05D0}\u{05D7}\u{05D5}\u{05D4}\u{002E}"),

/* -------------------------- Hindi -------------------------- */
new shaping( 
    "fontname=NotoSerifDevanagari-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=deva", 1, "Hindi", 
    "\u{0939}\u{093F}\u{0928}\u{094D}\u{0926}\u{0940}"),

new shaping( 
    "fontname=NotoSerifDevanagari-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=deva advancedlinebreak", 1, "Hindi",
    "\u{0905}\u{0928}\u{0941}\u{091A}\u{094D}\u{091B}\u{0947}\u{0926}" .
    "\u{0020}\u{0967}\u{002E}\u{0020}\u{0938}\u{092D}\u{0940}\u{0020}" .
    "\u{092E}\u{0928}\u{0941}\u{0937}\u{094D}\u{092F}\u{094B}\u{0902}" .
    "\u{0020}\u{0915}\u{094B}\u{0020}\u{0917}\u{094C}\u{0930}\u{0935}" .
    "\u{0020}\u{0914}\u{0930}\u{0020}\u{0905}\u{0927}\u{093F}\u{0915}" .
    "\u{093E}\u{0930}\u{094B}\u{0902}\u{0020}\u{0915}\u{0947}\u{0020}" .
    "\u{092E}\u{093E}\u{092E}\u{0932}\u{0947}\u{0020}\u{092E}\u{0947}" .
    "\u{0902}\u{0020}\u{091C}\u{0928}\u{094D}\u{092E}\u{091C}\u{093E}" .
    "\u{0924}\u{0020}\u{0938}\u{094D}\u{0935}\u{0924}\u{0928}\u{094D}" .
    "\u{0924}\u{094D}\u{0930}\u{0924}\u{093E}\u{0020}\u{0914}\u{0930}" .
    "\u{0020}\u{0938}\u{092E}\u{093E}\u{0928}\u{0924}\u{093E}\u{0020}" .
    "\u{092A}\u{094D}\u{0930}\u{093E}\u{092A}\u{094D}\u{0924}\u{0020}" .
    "\u{0939}\u{0948}\u{0020}\u{0964}\u{0020}\u{0909}\u{0928}\u{094D}" .
    "\u{0939}\u{0947}\u{0902}\u{0020}\u{092C}\u{0941}\u{0926}\u{094D}" .
    "\u{0918}\u{093F}\u{0020}\u{0914}\u{0930}\u{0020}\u{0905}\u{0928}" .
    "\u{094D}\u{0924}\u{0930}\u{093E}\u{0924}\u{094D}\u{092E}\u{093E}" .
    "\u{0020}\u{0915}\u{0940}\u{0020}\u{0926}\u{0947}\u{0928}\u{0020}" .
    "\u{092A}\u{094D}\u{0930}\u{093E}\u{092A}\u{094D}\u{0924}\u{0020}" .
    "\u{0939}\u{0948}\u{0020}\u{0914}\u{0930}\u{0020}\u{092A}\u{0930}" .
    "\u{0938}\u{094D}\u{092A}\u{0930}\u{0020}\u{0909}\u{0928}\u{094D}" .
    "\u{0939}\u{0947}\u{0902}\u{0020}\u{092D}\u{093E}\u{0908}\u{091A}" .
    "\u{093E}\u{0930}\u{0947}\u{0020}\u{0915}\u{0947}\u{0020}\u{092D}" .
    "\u{093E}\u{0935}\u{0020}\u{0938}\u{0947}\u{0020}\u{092C}\u{0930}" .
    "\u{094D}\u{0924}\u{093E}\u{0935}\u{0020}\u{0915}\u{0930}\u{0928}" .
    "\u{093E}\u{0020}\u{091A}\u{093E}\u{0939}\u{093F}\u{090F}\u{0020}" .
    "\u{0964}"),

/* -------------------------- Sanskrit -------------------------- */
new shaping( 
    "fontname=NotoSerifDevanagari-Regular fallbackfonts={ {fontname=NotoSerif-Regular} }",
    "shaping script=deva", 1, "Sanskrit",
    "\u{0938}\u{0902}\u{0938}\u{094D}\u{0915}\u{0943}\u{0924}\u{092E}" .
    "\u{094D}"),
);

$MAXROW = count($shapingsamples); 

try {
    $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");

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

    $p->set_info("Creator", "PDFlib starter sample");
    $p->set_info("Title", "starter_shaping");

    $table = 0;

    /* Create table header */
    for ($row=1, $col=1; $col <= $MAXCOL; $col++)
    {
        $optlist = sprintf(
       "fittextline={fontname=NotoSerif-Bold fontsize=14} " .
       "colwidth=%s", $col==1 ? "10%" : "45%" );
        $table = $p->add_table_cell($table, $col, $row, $header[$col-1],
                $optlist);
    }

    /* Create shaping samples */
    for ($row=2; $row < $MAXROW; $row++)
    {
        $col=1;

        /* Column 1: language name */
        $optlist = sprintf(
            "fittextline={fontname=NotoSerif-Regular fontsize=12}");
        $table = $p->add_table_cell($table, $col++, $row,
            $shapingsamples[$row]->language, $optlist);

        /* Column 2: raw text */
        $optlist = sprintf("%s fontsize=12 leading=150%% alignment=left", 
        $shapingsamples[$row]->fontopt);
        $tf = $p->create_textflow($shapingsamples[$row]->text, $optlist);
        $optlist = sprintf(
            "margin=4 fittextflow={verticalalign=top} textflow=%d", $tf);
        $table = $p->add_table_cell($table, $col++, $row, "", $optlist);

        /* Column 3: shaped and reordered text (Textline or Textflow) */
        if ($shapingsamples[$row]->textflow)
        {
            $optlist = sprintf("%s %s fontsize=12 leading=150%% alignment=left", 
            $shapingsamples[$row]->fontopt, $shapingsamples[$row]->optlist );
            $tf = $p->create_textflow($shapingsamples[$row]->text, $optlist);
            $optlist = sprintf(
                "margin=4 fittextflow={verticalalign=top} textflow=%d", $tf);
            $table = $p->add_table_cell($table, $col++, $row, "", $optlist);
        } else {
            $optlist = sprintf( "fittextline={%s %s fontsize=12 }", $shapingsamples[$row]->fontopt,
               $shapingsamples[$row]->optlist);
            $table = $p->add_table_cell($table, $col++, $row,
                     $shapingsamples[$row]->text, $optlist);
        }
    }

    /* ---------- Place the table on one or more pages ---------- */
    /*
     * 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");

        /* Shade every other row; draw lines for all table cells. */
        $optlist = sprintf( "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("Error: " . $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=starter_shaping.pdf");
    print $buf;

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

$p = 0;
?>