Beispiel #1
0
    /**
     * Allows to preserve some HTML formatting (limited support).<br />
     * IMPORTANT: The HTML must be well formatted - try to clean-up it using an application like HTML-Tidy before submitting.
     * Supported tags are: a, b, blockquote, br, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, img, li, ol, p, pre, small, span, strong, sub, sup, table, tcpdf, td, th, thead, tr, tt, u, ul
     * NOTE: all the HTML attributes must be enclosed in double-quote.
     * @param $html (string) text to display
     * @param $ln (boolean) if true add a new line after text (default = true)
     * @param $fill (boolean) Indicates if the background must be painted (true) or transparent (false).
     * @param $reseth (boolean) if true reset the last cell height (default false).
     * @param $cell (boolean) if true add the current left (or right for RTL) padding to each Write (default false).
     * @param $align (string) Allows to center or align the text. Possible values are:<ul><li>L : left align</li><li>C : center</li><li>R : right align</li><li>'' : empty string : left for LTR or right for RTL</li></ul>
     * @public
     */
    public function writeHTML($html, $ln = true, $fill = false, $reseth = false, $cell = false, $align = '')
    {
        $gvars = $this->getGraphicVars();
        // store current values
        $prev_cell_margin = $this->cell_margin;
        $prev_cell_padding = $this->cell_padding;
        $prevPage = $this->page;
        $prevlMargin = $this->lMargin;
        $prevrMargin = $this->rMargin;
        $curfontname = $this->FontFamily;
        $curfontstyle = $this->FontStyle;
        $curfontsize = $this->FontSizePt;
        $curfontascent = $this->getFontAscent($curfontname, $curfontstyle, $curfontsize);
        $curfontdescent = $this->getFontDescent($curfontname, $curfontstyle, $curfontsize);
        $curfontstretcing = $this->font_stretching;
        $curfonttracking = $this->font_spacing;
        $this->newline = true;
        $newline = true;
        $startlinepage = $this->page;
        $minstartliney = $this->y;
        $maxbottomliney = 0;
        $startlinex = $this->x;
        $startliney = $this->y;
        $yshift = 0;
        $loop = 0;
        $curpos = 0;
        $this_method_vars = array();
        $undo = false;
        $fontaligned = false;
        $reverse_dir = false;
        // true when the text direction is reversed
        $this->premode = false;
        if ($this->inxobj) {
            // we are inside an XObject template
            $pask = count($this->xobjects[$this->xobjid]['annotations']);
        } elseif (isset($this->PageAnnots[$this->page])) {
            $pask = count($this->PageAnnots[$this->page]);
        } else {
            $pask = 0;
        }
        if ($this->inxobj) {
            // we are inside an XObject template
            $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
        } elseif (!$this->InFooter) {
            if (isset($this->footerlen[$this->page])) {
                $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
            } else {
                $this->footerpos[$this->page] = $this->pagelen[$this->page];
            }
            $startlinepos = $this->footerpos[$this->page];
        } else {
            // we are inside the footer
            $startlinepos = $this->pagelen[$this->page];
        }
        $lalign = $align;
        $plalign = $align;
        if ($this->rtl) {
            $w = $this->x - $this->lMargin;
        } else {
            $w = $this->w - $this->rMargin - $this->x;
        }
        $w -= $this->cell_padding['L'] + $this->cell_padding['R'];
        if ($cell) {
            if ($this->rtl) {
                $this->x -= $this->cell_padding['R'];
                $this->lMargin += $this->cell_padding['R'];
            } else {
                $this->x += $this->cell_padding['L'];
                $this->rMargin += $this->cell_padding['L'];
            }
        }
        if ($this->customlistindent >= 0) {
            $this->listindent = $this->customlistindent;
        } else {
            $this->listindent = $this->GetStringWidth('000000');
        }
        $this->listindentlevel = 0;
        // save previous states
        $prev_cell_height_ratio = $this->cell_height_ratio;
        $prev_listnum = $this->listnum;
        $prev_listordered = $this->listordered;
        $prev_listcount = $this->listcount;
        $prev_lispacer = $this->lispacer;
        $this->listnum = 0;
        $this->listordered = array();
        $this->listcount = array();
        $this->lispacer = '';
        if (TCPDF_STATIC::empty_string($this->lasth) or $reseth) {
            // reset row height
            $this->resetLastH();
        }
        $dom = $this->getHtmlDomArray($html);
        $maxel = count($dom);
        $key = 0;
        $hidden_node_key = -1;
        while ($key < $maxel) {
            if ($dom[$key]['tag']) {
                if ($dom[$key]['opening']) {
                    if ($hidden_node_key <= 0 and $dom[$key]['hide']) {
                        // store the node key
                        $hidden_node_key = $key;
                    }
                } elseif ($hidden_node_key > 0 and $dom[$key]['parent'] == $hidden_node_key) {
                    // we have reached the closing tag of the hidden node
                    $hidden_node_key = 0;
                }
            }
            if ($hidden_node_key >= 0) {
                // skip this node
                ++$key;
                if ($hidden_node_key == 0) {
                    // reset hidden mode
                    $hidden_node_key = -1;
                }
                continue;
            }
            if ($dom[$key]['tag'] and isset($dom[$key]['attribute']['pagebreak'])) {
                // check for pagebreak
                if ($dom[$key]['attribute']['pagebreak'] == 'true' or $dom[$key]['attribute']['pagebreak'] == 'left' or $dom[$key]['attribute']['pagebreak'] == 'right') {
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
                    $this->checkPageBreak($this->PageBreakTrigger + 1);
                    $this->htmlvspace = $this->PageBreakTrigger + 1;
                }
                if ($dom[$key]['attribute']['pagebreak'] == 'left' and (!$this->rtl and $this->page % 2 == 0 or $this->rtl and $this->page % 2 != 0) or $dom[$key]['attribute']['pagebreak'] == 'right' and (!$this->rtl and $this->page % 2 != 0 or $this->rtl and $this->page % 2 == 0)) {
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
                    $this->checkPageBreak($this->PageBreakTrigger + 1);
                    $this->htmlvspace = $this->PageBreakTrigger + 1;
                }
            }
            if ($dom[$key]['tag'] and $dom[$key]['opening'] and isset($dom[$key]['attribute']['nobr']) and $dom[$key]['attribute']['nobr'] == 'true') {
                if (isset($dom[$dom[$key]['parent']]['attribute']['nobr']) and $dom[$dom[$key]['parent']]['attribute']['nobr'] == 'true') {
                    $dom[$key]['attribute']['nobr'] = false;
                } else {
                    // store current object
                    $this->startTransaction();
                    // save this method vars
                    $this_method_vars['html'] = $html;
                    $this_method_vars['ln'] = $ln;
                    $this_method_vars['fill'] = $fill;
                    $this_method_vars['reseth'] = $reseth;
                    $this_method_vars['cell'] = $cell;
                    $this_method_vars['align'] = $align;
                    $this_method_vars['gvars'] = $gvars;
                    $this_method_vars['prevPage'] = $prevPage;
                    $this_method_vars['prev_cell_margin'] = $prev_cell_margin;
                    $this_method_vars['prev_cell_padding'] = $prev_cell_padding;
                    $this_method_vars['prevlMargin'] = $prevlMargin;
                    $this_method_vars['prevrMargin'] = $prevrMargin;
                    $this_method_vars['curfontname'] = $curfontname;
                    $this_method_vars['curfontstyle'] = $curfontstyle;
                    $this_method_vars['curfontsize'] = $curfontsize;
                    $this_method_vars['curfontascent'] = $curfontascent;
                    $this_method_vars['curfontdescent'] = $curfontdescent;
                    $this_method_vars['curfontstretcing'] = $curfontstretcing;
                    $this_method_vars['curfonttracking'] = $curfonttracking;
                    $this_method_vars['minstartliney'] = $minstartliney;
                    $this_method_vars['maxbottomliney'] = $maxbottomliney;
                    $this_method_vars['yshift'] = $yshift;
                    $this_method_vars['startlinepage'] = $startlinepage;
                    $this_method_vars['startlinepos'] = $startlinepos;
                    $this_method_vars['startlinex'] = $startlinex;
                    $this_method_vars['startliney'] = $startliney;
                    $this_method_vars['newline'] = $newline;
                    $this_method_vars['loop'] = $loop;
                    $this_method_vars['curpos'] = $curpos;
                    $this_method_vars['pask'] = $pask;
                    $this_method_vars['lalign'] = $lalign;
                    $this_method_vars['plalign'] = $plalign;
                    $this_method_vars['w'] = $w;
                    $this_method_vars['prev_cell_height_ratio'] = $prev_cell_height_ratio;
                    $this_method_vars['prev_listnum'] = $prev_listnum;
                    $this_method_vars['prev_listordered'] = $prev_listordered;
                    $this_method_vars['prev_listcount'] = $prev_listcount;
                    $this_method_vars['prev_lispacer'] = $prev_lispacer;
                    $this_method_vars['fontaligned'] = $fontaligned;
                    $this_method_vars['key'] = $key;
                    $this_method_vars['dom'] = $dom;
                }
            }
            // print THEAD block
            if ($dom[$key]['value'] == 'tr' and isset($dom[$key]['thead']) and $dom[$key]['thead']) {
                if (isset($dom[$key]['parent']) and isset($dom[$dom[$key]['parent']]['thead']) and !TCPDF_STATIC::empty_string($dom[$dom[$key]['parent']]['thead'])) {
                    $this->inthead = true;
                    // print table header (thead)
                    $this->writeHTML($this->thead, false, false, false, false, '');
                    // check if we are on a new page or on a new column
                    if ($this->y < $this->start_transaction_y or $this->checkPageBreak($this->lasth, '', false)) {
                        // we are on a new page or on a new column and the total object height is less than the available vertical space.
                        // restore previous object
                        $this->rollbackTransaction(true);
                        // restore previous values
                        foreach ($this_method_vars as $vkey => $vval) {
                            ${$vkey} = $vval;
                        }
                        // disable table header
                        $tmp_thead = $this->thead;
                        $this->thead = '';
                        // add a page (or trig AcceptPageBreak() for multicolumn mode)
                        $pre_y = $this->y;
                        if (!$this->checkPageBreak($this->PageBreakTrigger + 1) and $this->y < $pre_y) {
                            // fix for multicolumn mode
                            $startliney = $this->y;
                        }
                        $this->start_transaction_page = $this->page;
                        $this->start_transaction_y = $this->y;
                        // restore table header
                        $this->thead = $tmp_thead;
                        // fix table border properties
                        if (isset($dom[$dom[$key]['parent']]['attribute']['cellspacing'])) {
                            $tmp_cellspacing = $this->getHTMLUnitToUnits($dom[$dom[$key]['parent']]['attribute']['cellspacing'], 1, 'px');
                        } elseif (isset($dom[$dom[$key]['parent']]['border-spacing'])) {
                            $tmp_cellspacing = $dom[$dom[$key]['parent']]['border-spacing']['V'];
                        } else {
                            $tmp_cellspacing = 0;
                        }
                        $dom[$dom[$key]['parent']]['borderposition']['page'] = $this->page;
                        $dom[$dom[$key]['parent']]['borderposition']['column'] = $this->current_column;
                        $dom[$dom[$key]['parent']]['borderposition']['y'] = $this->y + $tmp_cellspacing;
                        $xoffset = $this->x - $dom[$dom[$key]['parent']]['borderposition']['x'];
                        $dom[$dom[$key]['parent']]['borderposition']['x'] += $xoffset;
                        $dom[$dom[$key]['parent']]['borderposition']['xmax'] += $xoffset;
                        // print table header (thead)
                        $this->writeHTML($this->thead, false, false, false, false, '');
                    }
                }
                // move $key index forward to skip THEAD block
                while ($key < $maxel and !($dom[$key]['tag'] and $dom[$key]['opening'] and $dom[$key]['value'] == 'tr' and (!isset($dom[$key]['thead']) or !$dom[$key]['thead']) or $dom[$key]['tag'] and !$dom[$key]['opening'] and $dom[$key]['value'] == 'table')) {
                    ++$key;
                }
            }
            if ($dom[$key]['tag'] or $key == 0) {
                if (($dom[$key]['value'] == 'table' or $dom[$key]['value'] == 'tr') and isset($dom[$key]['align'])) {
                    $dom[$key]['align'] = $this->rtl ? 'R' : 'L';
                }
                // vertically align image in line
                if (!$this->newline and $dom[$key]['value'] == 'img' and isset($dom[$key]['height']) and $dom[$key]['height'] > 0) {
                    // get image height
                    $imgh = $this->getHTMLUnitToUnits($dom[$key]['height'], $this->lasth, 'px');
                    $autolinebreak = false;
                    if (isset($dom[$key]['width']) and $dom[$key]['width'] > 0) {
                        $imgw = $this->getHTMLUnitToUnits($dom[$key]['width'], 1, 'px', false);
                        if ($imgw <= $this->w - $this->lMargin - $this->rMargin - $this->cell_padding['L'] - $this->cell_padding['R'] and ($this->rtl and $this->x - $imgw < $this->lMargin + $this->cell_padding['L'] or !$this->rtl and $this->x + $imgw > $this->w - $this->rMargin - $this->cell_padding['R'])) {
                            // add automatic line break
                            $autolinebreak = true;
                            $this->Ln('', $cell);
                            if (!$dom[$key - 1]['tag'] and $dom[$key - 1]['value'] == ' ') {
                                // go back to evaluate this line break
                                --$key;
                            }
                        }
                    }
                    if (!$autolinebreak) {
                        if ($this->inPageBody()) {
                            $pre_y = $this->y;
                            // check for page break
                            if (!$this->checkPageBreak($imgh) and $this->y < $pre_y) {
                                // fix for multicolumn mode
                                $startliney = $this->y;
                            }
                        }
                        if ($this->page > $startlinepage) {
                            // fix line splitted over two pages
                            if (isset($this->footerlen[$startlinepage])) {
                                $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
                            }
                            // line to be moved one page forward
                            $pagebuff = $this->getPageBuffer($startlinepage);
                            $linebeg = substr($pagebuff, $startlinepos, $curpos - $startlinepos);
                            $tstart = substr($pagebuff, 0, $startlinepos);
                            $tend = substr($this->getPageBuffer($startlinepage), $curpos);
                            // remove line from previous page
                            $this->setPageBuffer($startlinepage, $tstart . '' . $tend);
                            $pagebuff = $this->getPageBuffer($this->page);
                            $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
                            $tend = substr($pagebuff, $this->cntmrk[$this->page]);
                            // add line start to current page
                            $yshift = $minstartliney - $this->y;
                            if ($fontaligned) {
                                $yshift += $curfontsize / $this->k;
                            }
                            $try = sprintf('1 0 0 1 0 %F cm', $yshift * $this->k);
                            $this->setPageBuffer($this->page, $tstart . "\nq\n" . $try . "\n" . $linebeg . "\nQ\n" . $tend);
                            // shift the annotations and links
                            if (isset($this->PageAnnots[$this->page])) {
                                $next_pask = count($this->PageAnnots[$this->page]);
                            } else {
                                $next_pask = 0;
                            }
                            if (isset($this->PageAnnots[$startlinepage])) {
                                foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
                                    if ($pak >= $pask) {
                                        $this->PageAnnots[$this->page][] = $pac;
                                        unset($this->PageAnnots[$startlinepage][$pak]);
                                        $npak = count($this->PageAnnots[$this->page]) - 1;
                                        $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
                                    }
                                }
                            }
                            $pask = $next_pask;
                            $startlinepos = $this->cntmrk[$this->page];
                            $startlinepage = $this->page;
                            $startliney = $this->y;
                            $this->newline = false;
                        }
                        $this->y += ($curfontsize * $this->cell_height_ratio / $this->k + $curfontascent - $curfontdescent) / 2 - $imgh;
                        $minstartliney = min($this->y, $minstartliney);
                        $maxbottomliney = $startliney + $this->FontSize * $this->cell_height_ratio;
                    }
                } elseif (isset($dom[$key]['fontname']) or isset($dom[$key]['fontstyle']) or isset($dom[$key]['fontsize']) or isset($dom[$key]['line-height'])) {
                    // account for different font size
                    $pfontname = $curfontname;
                    $pfontstyle = $curfontstyle;
                    $pfontsize = $curfontsize;
                    $fontname = isset($dom[$key]['fontname']) ? $dom[$key]['fontname'] : $curfontname;
                    $fontstyle = isset($dom[$key]['fontstyle']) ? $dom[$key]['fontstyle'] : $curfontstyle;
                    $fontsize = isset($dom[$key]['fontsize']) ? $dom[$key]['fontsize'] : $curfontsize;
                    $fontascent = $this->getFontAscent($fontname, $fontstyle, $fontsize);
                    $fontdescent = $this->getFontDescent($fontname, $fontstyle, $fontsize);
                    if ($fontname != $curfontname or $fontstyle != $curfontstyle or $fontsize != $curfontsize or $this->cell_height_ratio != $dom[$key]['line-height'] or $dom[$key]['tag'] and $dom[$key]['opening'] and $dom[$key]['value'] == 'li') {
                        if ($key < $maxel - 1 and ($dom[$key]['tag'] and $dom[$key]['opening'] and $dom[$key]['value'] == 'li' or $this->cell_height_ratio != $dom[$key]['line-height'] or !$this->newline and is_numeric($fontsize) and is_numeric($curfontsize) and $fontsize >= 0 and $curfontsize >= 0 and $fontsize != $curfontsize)) {
                            if ($this->page > $startlinepage) {
                                // fix lines splitted over two pages
                                if (isset($this->footerlen[$startlinepage])) {
                                    $curpos = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
                                }
                                // line to be moved one page forward
                                $pagebuff = $this->getPageBuffer($startlinepage);
                                $linebeg = substr($pagebuff, $startlinepos, $curpos - $startlinepos);
                                $tstart = substr($pagebuff, 0, $startlinepos);
                                $tend = substr($this->getPageBuffer($startlinepage), $curpos);
                                // remove line start from previous page
                                $this->setPageBuffer($startlinepage, $tstart . '' . $tend);
                                $pagebuff = $this->getPageBuffer($this->page);
                                $tstart = substr($pagebuff, 0, $this->cntmrk[$this->page]);
                                $tend = substr($pagebuff, $this->cntmrk[$this->page]);
                                // add line start to current page
                                $yshift = $minstartliney - $this->y;
                                $try = sprintf('1 0 0 1 0 %F cm', $yshift * $this->k);
                                $this->setPageBuffer($this->page, $tstart . "\nq\n" . $try . "\n" . $linebeg . "\nQ\n" . $tend);
                                // shift the annotations and links
                                if (isset($this->PageAnnots[$this->page])) {
                                    $next_pask = count($this->PageAnnots[$this->page]);
                                } else {
                                    $next_pask = 0;
                                }
                                if (isset($this->PageAnnots[$startlinepage])) {
                                    foreach ($this->PageAnnots[$startlinepage] as $pak => $pac) {
                                        if ($pak >= $pask) {
                                            $this->PageAnnots[$this->page][] = $pac;
                                            unset($this->PageAnnots[$startlinepage][$pak]);
                                            $npak = count($this->PageAnnots[$this->page]) - 1;
                                            $this->PageAnnots[$this->page][$npak]['y'] -= $yshift;
                                        }
                                    }
                                }
                                $pask = $next_pask;
                                $startlinepos = $this->cntmrk[$this->page];
                                $startlinepage = $this->page;
                                $startliney = $this->y;
                            }
                            if (!isset($dom[$key]['line-height'])) {
                                $dom[$key]['line-height'] = $this->cell_height_ratio;
                            }
                            if (!$dom[$key]['block']) {
                                if (!(isset($dom[$key + 1]) and $dom[$key + 1]['tag'] and !$dom[$key + 1]['opening'] and $dom[$key + 1]['value'] != 'li' and $dom[$key]['tag'] and !$dom[$key]['opening'])) {
                                    $this->y += (($curfontsize * $this->cell_height_ratio - $fontsize * $dom[$key]['line-height']) / $this->k + $curfontascent - $fontascent - $curfontdescent + $fontdescent) / 2;
                                }
                                if ($dom[$key]['value'] != 'sup' and $dom[$key]['value'] != 'sub') {
                                    $current_line_align_data = array($key, $minstartliney, $maxbottomliney);
                                    if (isset($line_align_data) and ($line_align_data[0] == $key - 1 or $line_align_data[0] == $key - 2 and isset($dom[$key - 1]) and preg_match('/^([\\s]+)$/', $dom[$key - 1]['value']) > 0)) {
                                        $minstartliney = min($this->y, $line_align_data[1]);
                                        $maxbottomliney = max($this->y + $fontsize * $this->cell_height_ratio / $this->k, $line_align_data[2]);
                                    } else {
                                        $minstartliney = min($this->y, $minstartliney);
                                        $maxbottomliney = max($this->y + $fontsize * $this->cell_height_ratio / $this->k, $maxbottomliney);
                                    }
                                    $line_align_data = $current_line_align_data;
                                }
                            }
                            $this->cell_height_ratio = $dom[$key]['line-height'];
                            $fontaligned = true;
                        }
                        $this->SetFont($fontname, $fontstyle, $fontsize);
                        // reset row height
                        $this->resetLastH();
                        $curfontname = $fontname;
                        $curfontstyle = $fontstyle;
                        $curfontsize = $fontsize;
                        $curfontascent = $fontascent;
                        $curfontdescent = $fontdescent;
                    }
                }
                // set text rendering mode
                $textstroke = isset($dom[$key]['stroke']) ? $dom[$key]['stroke'] : $this->textstrokewidth;
                $textfill = isset($dom[$key]['fill']) ? $dom[$key]['fill'] : $this->textrendermode % 2 == 0;
                $textclip = isset($dom[$key]['clip']) ? $dom[$key]['clip'] : $this->textrendermode > 3;
                $this->setTextRenderingMode($textstroke, $textfill, $textclip);
                if (isset($dom[$key]['font-stretch']) and $dom[$key]['font-stretch'] !== false) {
                    $this->setFontStretching($dom[$key]['font-stretch']);
                }
                if (isset($dom[$key]['letter-spacing']) and $dom[$key]['letter-spacing'] !== false) {
                    $this->setFontSpacing($dom[$key]['letter-spacing']);
                }
                if ($plalign == 'J' and $dom[$key]['block']) {
                    $plalign = '';
                }
                // get current position on page buffer
                $curpos = $this->pagelen[$startlinepage];
                if (isset($dom[$key]['bgcolor']) and $dom[$key]['bgcolor'] !== false) {
                    $this->SetFillColorArray($dom[$key]['bgcolor']);
                    $wfill = true;
                } else {
                    $wfill = $fill | false;
                }
                if (isset($dom[$key]['fgcolor']) and $dom[$key]['fgcolor'] !== false) {
                    $this->SetTextColorArray($dom[$key]['fgcolor']);
                }
                if (isset($dom[$key]['strokecolor']) and $dom[$key]['strokecolor'] !== false) {
                    $this->SetDrawColorArray($dom[$key]['strokecolor']);
                }
                if (isset($dom[$key]['align'])) {
                    $lalign = $dom[$key]['align'];
                }
                if (TCPDF_STATIC::empty_string($lalign)) {
                    $lalign = $align;
                }
            }
            // align lines
            if ($this->newline and strlen($dom[$key]['value']) > 0 and $dom[$key]['value'] != 'td' and $dom[$key]['value'] != 'th') {
                $newline = true;
                $fontaligned = false;
                // we are at the beginning of a new line
                if (isset($startlinex)) {
                    $yshift = $minstartliney - $startliney;
                    if ($yshift > 0 or $this->page > $startlinepage) {
                        $yshift = 0;
                    }
                    $t_x = 0;
                    // the last line must be shifted to be aligned as requested
                    $linew = abs($this->endlinex - $startlinex);
                    if ($this->inxobj) {
                        // we are inside an XObject template
                        $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
                        if (isset($opentagpos)) {
                            $midpos = $opentagpos;
                        } else {
                            $midpos = 0;
                        }
                        if ($midpos > 0) {
                            $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, $midpos - $startlinepos);
                            $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
                        } else {
                            $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
                            $pend = '';
                        }
                    } else {
                        $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
                        if (isset($opentagpos) and isset($this->footerlen[$startlinepage]) and !$this->InFooter) {
                            $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
                            $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
                        } elseif (isset($opentagpos)) {
                            $midpos = $opentagpos;
                        } elseif (isset($this->footerlen[$startlinepage]) and !$this->InFooter) {
                            $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
                            $midpos = $this->footerpos[$startlinepage];
                        } else {
                            $midpos = 0;
                        }
                        if ($midpos > 0) {
                            $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, $midpos - $startlinepos);
                            $pend = substr($this->getPageBuffer($startlinepage), $midpos);
                        } else {
                            $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
                            $pend = '';
                        }
                    }
                    if (isset($plalign) and ($plalign == 'C' or $plalign == 'J' or $plalign == 'R' and !$this->rtl or $plalign == 'L' and $this->rtl)) {
                        // calculate shifting amount
                        $tw = $w;
                        if ($plalign == 'J' and $this->isRTLTextDir() and $this->num_columns > 1) {
                            $tw += $this->cell_padding['R'];
                        }
                        if ($this->lMargin != $prevlMargin) {
                            $tw += $prevlMargin - $this->lMargin;
                        }
                        if ($this->rMargin != $prevrMargin) {
                            $tw += $prevrMargin - $this->rMargin;
                        }
                        $one_space_width = $this->GetStringWidth(chr(32));
                        $no = 0;
                        // number of spaces on a line contained on a single block
                        if ($this->isRTLTextDir()) {
                            // RTL
                            // remove left space if exist
                            $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
                            if ($pos1 > 0) {
                                $pos1 = intval($pos1);
                                if ($this->isUnicodeFont()) {
                                    $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[(' . chr(0) . chr(32)));
                                    $spacelen = 2;
                                } else {
                                    $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[(' . chr(32)));
                                    $spacelen = 1;
                                }
                                if ($pos1 == $pos2) {
                                    $pmid = substr($pmid, 0, $pos1 + 2) . substr($pmid, $pos1 + 2 + $spacelen);
                                    if (substr($pmid, $pos1, 4) == '[()]') {
                                        $linew -= $one_space_width;
                                    } elseif ($pos1 == strpos($pmid, '[(')) {
                                        $no = 1;
                                    }
                                }
                            }
                        } else {
                            // LTR
                            // remove right space if exist
                            $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
                            if ($pos1 > 0) {
                                $pos1 = intval($pos1);
                                if ($this->isUnicodeFont()) {
                                    $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0) . chr(32) . ')]')) + 2;
                                    $spacelen = 2;
                                } else {
                                    $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32) . ')]')) + 1;
                                    $spacelen = 1;
                                }
                                if ($pos1 == $pos2) {
                                    $pmid = substr($pmid, 0, $pos1 - $spacelen) . substr($pmid, $pos1);
                                    $linew -= $one_space_width;
                                }
                            }
                        }
                        $mdiff = $tw - $linew;
                        if ($plalign == 'C') {
                            if ($this->rtl) {
                                $t_x = -($mdiff / 2);
                            } else {
                                $t_x = $mdiff / 2;
                            }
                        } elseif ($plalign == 'R') {
                            // right alignment on LTR document
                            $t_x = $mdiff;
                        } elseif ($plalign == 'L') {
                            // left alignment on RTL document
                            $t_x = -$mdiff;
                        } elseif ($plalign == 'J' and $plalign == $lalign) {
                            // Justification
                            if ($this->isRTLTextDir()) {
                                // align text on the left
                                $t_x = -$mdiff;
                            }
                            $ns = 0;
                            // number of spaces
                            $pmidtemp = $pmid;
                            // escape special characters
                            $pmidtemp = preg_replace('/[\\\\][\\(]/x', '\\#!#OP#!#', $pmidtemp);
                            $pmidtemp = preg_replace('/[\\\\][\\)]/x', '\\#!#CP#!#', $pmidtemp);
                            // search spaces
                            if (preg_match_all('/\\[\\(([^\\)]*)\\)\\]/x', $pmidtemp, $lnstring, PREG_PATTERN_ORDER)) {
                                $spacestr = $this->getSpaceString();
                                $maxkk = count($lnstring[1]) - 1;
                                for ($kk = 0; $kk <= $maxkk; ++$kk) {
                                    // restore special characters
                                    $lnstring[1][$kk] = str_replace('#!#OP#!#', '(', $lnstring[1][$kk]);
                                    $lnstring[1][$kk] = str_replace('#!#CP#!#', ')', $lnstring[1][$kk]);
                                    // store number of spaces on the strings
                                    $lnstring[2][$kk] = substr_count($lnstring[1][$kk], $spacestr);
                                    // count total spaces on line
                                    $ns += $lnstring[2][$kk];
                                    $lnstring[3][$kk] = $ns;
                                }
                                if ($ns == 0) {
                                    $ns = 1;
                                }
                                // calculate additional space to add to each existing space
                                $spacewidth = $mdiff / ($ns - $no) * $this->k;
                                if ($this->FontSize <= 0) {
                                    $this->FontSize = 1;
                                }
                                $spacewidthu = -1000 * ($mdiff + ($ns + $no) * $one_space_width) / $ns / $this->FontSize;
                                if ($this->font_spacing != 0) {
                                    // fixed spacing mode
                                    $osw = -1000 * $this->font_spacing / $this->FontSize;
                                    $spacewidthu += $osw;
                                }
                                $nsmax = $ns;
                                $ns = 0;
                                reset($lnstring);
                                $offset = 0;
                                $strcount = 0;
                                $prev_epsposbeg = 0;
                                $textpos = 0;
                                if ($this->isRTLTextDir()) {
                                    $textpos = $this->wPt;
                                }
                                global $spacew;
                                while (preg_match('/([0-9\\.\\+\\-]*)[\\s](Td|cm|m|l|c|re)[\\s]/x', $pmid, $strpiece, PREG_OFFSET_CAPTURE, $offset) == 1) {
                                    // check if we are inside a string section '[( ... )]'
                                    $stroffset = strpos($pmid, '[(', $offset);
                                    if ($stroffset !== false and $stroffset <= $strpiece[2][1]) {
                                        // set offset to the end of string section
                                        $offset = strpos($pmid, ')]', $stroffset);
                                        while ($offset !== false and $pmid[$offset - 1] == '\\') {
                                            $offset = strpos($pmid, ')]', $offset + 1);
                                        }
                                        if ($offset === false) {
                                            $this->Error('HTML Justification: malformed PDF code.');
                                        }
                                        continue;
                                    }
                                    if ($this->isRTLTextDir()) {
                                        $spacew = $spacewidth * ($nsmax - $ns);
                                    } else {
                                        $spacew = $spacewidth * $ns;
                                    }
                                    $offset = $strpiece[2][1] + strlen($strpiece[2][0]);
                                    $epsposbeg = strpos($pmid, 'q' . $this->epsmarker, $offset);
                                    $epsposend = strpos($pmid, $this->epsmarker . 'Q', $offset) + strlen($this->epsmarker . 'Q');
                                    if ($epsposbeg > 0 and $epsposend > 0 and $offset > $epsposbeg and $offset < $epsposend or $epsposbeg === false and $epsposend > 0 and $offset < $epsposend) {
                                        // shift EPS images
                                        $trx = sprintf('1 0 0 1 %F 0 cm', $spacew);
                                        $epsposbeg = strpos($pmid, 'q' . $this->epsmarker, $prev_epsposbeg - 6);
                                        $pmid_b = substr($pmid, 0, $epsposbeg);
                                        $pmid_m = substr($pmid, $epsposbeg, $epsposend - $epsposbeg);
                                        $pmid_e = substr($pmid, $epsposend);
                                        $pmid = $pmid_b . "\nq\n" . $trx . "\n" . $pmid_m . "\nQ\n" . $pmid_e;
                                        $offset = $epsposend;
                                        continue;
                                    }
                                    $prev_epsposbeg = $epsposbeg;
                                    $currentxpos = 0;
                                    // shift blocks of code
                                    switch ($strpiece[2][0]) {
                                        case 'Td':
                                        case 'cm':
                                        case 'm':
                                        case 'l':
                                            // get current X position
                                            preg_match('/([0-9\\.\\+\\-]*)[\\s](' . $strpiece[1][0] . ')[\\s](' . $strpiece[2][0] . ')([\\s]*)/x', $pmid, $xmatches);
                                            $currentxpos = $xmatches[1];
                                            $textpos = $currentxpos;
                                            if ($strcount <= $maxkk and $strpiece[2][0] == 'Td') {
                                                $ns = $lnstring[3][$strcount];
                                                if ($this->isRTLTextDir()) {
                                                    $spacew = $spacewidth * ($nsmax - $ns);
                                                }
                                                ++$strcount;
                                            }
                                            // justify block
                                            $pmid = preg_replace_callback('/([0-9\\.\\+\\-]*)[\\s](' . $strpiece[1][0] . ')[\\s](' . $strpiece[2][0] . ')([\\s]*)/x', create_function('$matches', 'global $spacew;
												$newx = sprintf("%F",(floatval($matches[1]) + $spacew));
												return "".$newx." ".$matches[2]." x*#!#*x".$matches[3].$matches[4];'), $pmid, 1);
                                            break;
                                        case 're':
                                            // justify block
                                            if (!TCPDF_STATIC::empty_string($this->lispacer)) {
                                                $this->lispacer = '';
                                                continue;
                                            }
                                            preg_match('/([0-9\\.\\+\\-]*)[\\s]([0-9\\.\\+\\-]*)[\\s]([0-9\\.\\+\\-]*)[\\s](' . $strpiece[1][0] . ')[\\s](re)([\\s]*)/x', $pmid, $xmatches);
                                            $currentxpos = $xmatches[1];
                                            global $x_diff, $w_diff;
                                            $x_diff = 0;
                                            $w_diff = 0;
                                            if ($this->isRTLTextDir()) {
                                                // RTL
                                                if ($currentxpos < $textpos) {
                                                    $x_diff = $spacewidth * ($nsmax - $lnstring[3][$strcount]);
                                                    $w_diff = $spacewidth * $lnstring[2][$strcount];
                                                } else {
                                                    if ($strcount > 0) {
                                                        $x_diff = $spacewidth * ($nsmax - $lnstring[3][$strcount - 1]);
                                                        $w_diff = $spacewidth * $lnstring[2][$strcount - 1];
                                                    }
                                                }
                                            } else {
                                                // LTR
                                                if ($currentxpos > $textpos) {
                                                    if ($strcount > 0) {
                                                        $x_diff = $spacewidth * $lnstring[3][$strcount - 1];
                                                    }
                                                    $w_diff = $spacewidth * $lnstring[2][$strcount];
                                                } else {
                                                    if ($strcount > 1) {
                                                        $x_diff = $spacewidth * $lnstring[3][$strcount - 2];
                                                    }
                                                    if ($strcount > 0) {
                                                        $w_diff = $spacewidth * $lnstring[2][$strcount - 1];
                                                    }
                                                }
                                            }
                                            $pmid = preg_replace_callback('/(' . $xmatches[1] . ')[\\s](' . $xmatches[2] . ')[\\s](' . $xmatches[3] . ')[\\s](' . $strpiece[1][0] . ')[\\s](re)([\\s]*)/x', create_function('$matches', 'global $x_diff, $w_diff;
												$newx = sprintf("%F",(floatval($matches[1]) + $x_diff));
												$neww = sprintf("%F",(floatval($matches[3]) + $w_diff));
												return "".$newx." ".$matches[2]." ".$neww." ".$matches[4]." x*#!#*x".$matches[5].$matches[6];'), $pmid, 1);
                                            break;
                                        case 'c':
                                            // get current X position
                                            preg_match('/([0-9\\.\\+\\-]*)[\\s]([0-9\\.\\+\\-]*)[\\s]([0-9\\.\\+\\-]*)[\\s]([0-9\\.\\+\\-]*)[\\s]([0-9\\.\\+\\-]*)[\\s](' . $strpiece[1][0] . ')[\\s](c)([\\s]*)/x', $pmid, $xmatches);
                                            $currentxpos = $xmatches[1];
                                            // justify block
                                            $pmid = preg_replace_callback('/(' . $xmatches[1] . ')[\\s](' . $xmatches[2] . ')[\\s](' . $xmatches[3] . ')[\\s](' . $xmatches[4] . ')[\\s](' . $xmatches[5] . ')[\\s](' . $strpiece[1][0] . ')[\\s](c)([\\s]*)/x', create_function('$matches', 'global $spacew;
												$newx1 = sprintf("%F",(floatval($matches[1]) + $spacew));
												$newx2 = sprintf("%F",(floatval($matches[3]) + $spacew));
												$newx3 = sprintf("%F",(floatval($matches[5]) + $spacew));
												return "".$newx1." ".$matches[2]." ".$newx2." ".$matches[4]." ".$newx3." ".$matches[6]." x*#!#*x".$matches[7].$matches[8];'), $pmid, 1);
                                            break;
                                    }
                                    // shift the annotations and links
                                    $cxpos = $currentxpos / $this->k;
                                    $lmpos = $this->lMargin + $this->cell_padding['L'] + $this->feps;
                                    if ($this->inxobj) {
                                        // we are inside an XObject template
                                        foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
                                            if ($pac['y'] >= $minstartliney and $pac['x'] * $this->k >= $currentxpos - $this->feps and $pac['x'] * $this->k <= $currentxpos + $this->feps) {
                                                if ($cxpos > $lmpos) {
                                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $spacew / $this->k;
                                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += $spacewidth * $pac['numspaces'] / $this->k;
                                                } else {
                                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['w'] += $spacewidth * $pac['numspaces'] / $this->k;
                                                }
                                                break;
                                            }
                                        }
                                    } elseif (isset($this->PageAnnots[$this->page])) {
                                        foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
                                            if ($pac['y'] >= $minstartliney and $pac['x'] * $this->k >= $currentxpos - $this->feps and $pac['x'] * $this->k <= $currentxpos + $this->feps) {
                                                if ($cxpos > $lmpos) {
                                                    $this->PageAnnots[$this->page][$pak]['x'] += $spacew / $this->k;
                                                    $this->PageAnnots[$this->page][$pak]['w'] += $spacewidth * $pac['numspaces'] / $this->k;
                                                } else {
                                                    $this->PageAnnots[$this->page][$pak]['w'] += $spacewidth * $pac['numspaces'] / $this->k;
                                                }
                                                break;
                                            }
                                        }
                                    }
                                }
                                // end of while
                                // remove markers
                                $pmid = str_replace('x*#!#*x', '', $pmid);
                                if ($this->isUnicodeFont()) {
                                    // multibyte characters
                                    $spacew = $spacewidthu;
                                    if ($this->font_stretching != 100) {
                                        // word spacing is affected by stretching
                                        $spacew /= $this->font_stretching / 100;
                                    }
                                    $pmidtemp = $pmid;
                                    // escape special characters
                                    $pmidtemp = preg_replace('/[\\\\][\\(]/x', '\\#!#OP#!#', $pmidtemp);
                                    $pmidtemp = preg_replace('/[\\\\][\\)]/x', '\\#!#CP#!#', $pmidtemp);
                                    $pmid = preg_replace_callback("/\\[\\(([^\\)]*)\\)\\]/x", create_function('$matches', 'global $spacew;
												$matches[1] = str_replace("#!#OP#!#", "(", $matches[1]);
												$matches[1] = str_replace("#!#CP#!#", ")", $matches[1]);
												return "[(".str_replace(chr(0).chr(32), ") ".sprintf("%F", $spacew)." (", $matches[1]).")]";'), $pmidtemp);
                                    if ($this->inxobj) {
                                        // we are inside an XObject template
                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart . "\n" . $pmid . "\n" . $pend;
                                    } else {
                                        $this->setPageBuffer($startlinepage, $pstart . "\n" . $pmid . "\n" . $pend);
                                    }
                                    $endlinepos = strlen($pstart . "\n" . $pmid . "\n");
                                } else {
                                    // non-unicode (single-byte characters)
                                    if ($this->font_stretching != 100) {
                                        // word spacing (Tw) is affected by stretching
                                        $spacewidth /= $this->font_stretching / 100;
                                    }
                                    $rs = sprintf('%F Tw', $spacewidth);
                                    $pmid = preg_replace("/\\[\\(/x", $rs . ' [(', $pmid);
                                    if ($this->inxobj) {
                                        // we are inside an XObject template
                                        $this->xobjects[$this->xobjid]['outdata'] = $pstart . "\n" . $pmid . "\nBT 0 Tw ET\n" . $pend;
                                    } else {
                                        $this->setPageBuffer($startlinepage, $pstart . "\n" . $pmid . "\nBT 0 Tw ET\n" . $pend);
                                    }
                                    $endlinepos = strlen($pstart . "\n" . $pmid . "\nBT 0 Tw ET\n");
                                }
                            }
                        }
                        // end of J
                    }
                    // end if $startlinex
                    if ($t_x != 0 or $yshift < 0) {
                        // shift the line
                        $trx = sprintf('1 0 0 1 %F %F cm', $t_x * $this->k, $yshift * $this->k);
                        $pstart .= "\nq\n" . $trx . "\n" . $pmid . "\nQ\n";
                        $endlinepos = strlen($pstart);
                        if ($this->inxobj) {
                            // we are inside an XObject template
                            $this->xobjects[$this->xobjid]['outdata'] = $pstart . $pend;
                            foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
                                if ($pak >= $pask) {
                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
                                    $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
                                }
                            }
                        } else {
                            $this->setPageBuffer($startlinepage, $pstart . $pend);
                            // shift the annotations and links
                            if (isset($this->PageAnnots[$this->page])) {
                                foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
                                    if ($pak >= $pask) {
                                        $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
                                        $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
                                    }
                                }
                            }
                        }
                        $this->y -= $yshift;
                    }
                }
                $pbrk = $this->checkPageBreak($this->lasth);
                $this->newline = false;
                $startlinex = $this->x;
                $startliney = $this->y;
                if ($dom[$dom[$key]['parent']]['value'] == 'sup') {
                    $startliney -= 0.3 * $this->FontSizePt / $this->k;
                } elseif ($dom[$dom[$key]['parent']]['value'] == 'sub') {
                    $startliney -= $this->FontSizePt / 0.7 / $this->k;
                } else {
                    $minstartliney = $startliney;
                    $maxbottomliney = $this->y + $fontsize * $this->cell_height_ratio / $this->k;
                }
                $startlinepage = $this->page;
                if (isset($endlinepos) and !$pbrk) {
                    $startlinepos = $endlinepos;
                } else {
                    if ($this->inxobj) {
                        // we are inside an XObject template
                        $startlinepos = strlen($this->xobjects[$this->xobjid]['outdata']);
                    } elseif (!$this->InFooter) {
                        if (isset($this->footerlen[$this->page])) {
                            $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
                        } else {
                            $this->footerpos[$this->page] = $this->pagelen[$this->page];
                        }
                        $startlinepos = $this->footerpos[$this->page];
                    } else {
                        $startlinepos = $this->pagelen[$this->page];
                    }
                }
                unset($endlinepos);
                $plalign = $lalign;
                if (isset($this->PageAnnots[$this->page])) {
                    $pask = count($this->PageAnnots[$this->page]);
                } else {
                    $pask = 0;
                }
                if (!($dom[$key]['tag'] and !$dom[$key]['opening'] and $dom[$key]['value'] == 'table' and isset($this->emptypagemrk[$this->page]) and $this->emptypagemrk[$this->page] == $this->pagelen[$this->page])) {
                    $this->SetFont($fontname, $fontstyle, $fontsize);
                    if ($wfill) {
                        $this->SetFillColorArray($this->bgcolor);
                    }
                }
            }
            // end newline
            if (isset($opentagpos)) {
                unset($opentagpos);
            }
            if ($dom[$key]['tag']) {
                if ($dom[$key]['opening']) {
                    // get text indentation (if any)
                    if (isset($dom[$key]['text-indent']) and $dom[$key]['block']) {
                        $this->textindent = $dom[$key]['text-indent'];
                        $this->newline = true;
                    }
                    // table
                    if ($dom[$key]['value'] == 'table' and isset($dom[$key]['cols']) and $dom[$key]['cols'] > 0) {
                        // available page width
                        if ($this->rtl) {
                            $wtmp = $this->x - $this->lMargin;
                        } else {
                            $wtmp = $this->w - $this->rMargin - $this->x;
                        }
                        // get cell spacing
                        if (isset($dom[$key]['attribute']['cellspacing'])) {
                            $clsp = $this->getHTMLUnitToUnits($dom[$key]['attribute']['cellspacing'], 1, 'px');
                            $cellspacing = array('H' => $clsp, 'V' => $clsp);
                        } elseif (isset($dom[$key]['border-spacing'])) {
                            $cellspacing = $dom[$key]['border-spacing'];
                        } else {
                            $cellspacing = array('H' => 0, 'V' => 0);
                        }
                        // table width
                        if (isset($dom[$key]['width'])) {
                            $table_width = $this->getHTMLUnitToUnits($dom[$key]['width'], $wtmp, 'px');
                        } else {
                            $table_width = $wtmp;
                        }
                        $table_width -= 2 * $cellspacing['H'];
                        if (!$this->inthead) {
                            $this->y += $cellspacing['V'];
                        }
                        if ($this->rtl) {
                            $cellspacingx = -$cellspacing['H'];
                        } else {
                            $cellspacingx = $cellspacing['H'];
                        }
                        // total table width without cellspaces
                        $table_columns_width = $table_width - $cellspacing['H'] * ($dom[$key]['cols'] - 1);
                        // minimum column width
                        $table_min_column_width = $table_columns_width / $dom[$key]['cols'];
                        // array of custom column widths
                        $table_colwidths = array_fill(0, $dom[$key]['cols'], $table_min_column_width);
                    }
                    // table row
                    if ($dom[$key]['value'] == 'tr') {
                        // reset column counter
                        $colid = 0;
                    }
                    // table cell
                    if ($dom[$key]['value'] == 'td' or $dom[$key]['value'] == 'th') {
                        $trid = $dom[$key]['parent'];
                        $table_el = $dom[$trid]['parent'];
                        if (!isset($dom[$table_el]['cols'])) {
                            $dom[$table_el]['cols'] = $dom[$trid]['cols'];
                        }
                        // store border info
                        $tdborder = 0;
                        if (isset($dom[$key]['border']) and !empty($dom[$key]['border'])) {
                            $tdborder = $dom[$key]['border'];
                        }
                        $colspan = intval($dom[$key]['attribute']['colspan']);
                        if ($colspan <= 0) {
                            $colspan = 1;
                        }
                        $old_cell_padding = $this->cell_padding;
                        if (isset($dom[$dom[$trid]['parent']]['attribute']['cellpadding'])) {
                            $crclpd = $this->getHTMLUnitToUnits($dom[$dom[$trid]['parent']]['attribute']['cellpadding'], 1, 'px');
                            $current_cell_padding = array('L' => $crclpd, 'T' => $crclpd, 'R' => $crclpd, 'B' => $crclpd);
                        } elseif (isset($dom[$dom[$trid]['parent']]['padding'])) {
                            $current_cell_padding = $dom[$dom[$trid]['parent']]['padding'];
                        } else {
                            $current_cell_padding = array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0);
                        }
                        $this->cell_padding = $current_cell_padding;
                        if (isset($dom[$key]['height'])) {
                            // minimum cell height
                            $cellh = $this->getHTMLUnitToUnits($dom[$key]['height'], 0, 'px');
                        } else {
                            $cellh = 0;
                        }
                        if (isset($dom[$key]['content'])) {
                            $cell_content = stripslashes($dom[$key]['content']);
                        } else {
                            $cell_content = '&nbsp;';
                        }
                        $tagtype = $dom[$key]['value'];
                        $parentid = $key;
                        while ($key < $maxel and !($dom[$key]['tag'] and !$dom[$key]['opening'] and $dom[$key]['value'] == $tagtype and $dom[$key]['parent'] == $parentid)) {
                            // move $key index forward
                            ++$key;
                        }
                        if (!isset($dom[$trid]['startpage'])) {
                            $dom[$trid]['startpage'] = $this->page;
                        } else {
                            $this->setPage($dom[$trid]['startpage']);
                        }
                        if (!isset($dom[$trid]['startcolumn'])) {
                            $dom[$trid]['startcolumn'] = $this->current_column;
                        } elseif ($this->current_column != $dom[$trid]['startcolumn']) {
                            $tmpx = $this->x;
                            $this->selectColumn($dom[$trid]['startcolumn']);
                            $this->x = $tmpx;
                        }
                        if (!isset($dom[$trid]['starty'])) {
                            $dom[$trid]['starty'] = $this->y;
                        } else {
                            $this->y = $dom[$trid]['starty'];
                        }
                        if (!isset($dom[$trid]['startx'])) {
                            $dom[$trid]['startx'] = $this->x;
                            $this->x += $cellspacingx;
                        } else {
                            $this->x += $cellspacingx / 2;
                        }
                        if (isset($dom[$parentid]['attribute']['rowspan'])) {
                            $rowspan = intval($dom[$parentid]['attribute']['rowspan']);
                        } else {
                            $rowspan = 1;
                        }
                        // skip row-spanned cells started on the previous rows
                        if (isset($dom[$table_el]['rowspans'])) {
                            $rsk = 0;
                            $rskmax = count($dom[$table_el]['rowspans']);
                            while ($rsk < $rskmax) {
                                $trwsp = $dom[$table_el]['rowspans'][$rsk];
                                $rsstartx = $trwsp['startx'];
                                $rsendx = $trwsp['endx'];
                                // account for margin changes
                                if ($trwsp['startpage'] < $this->page) {
                                    if ($this->rtl and $this->pagedim[$this->page]['orm'] != $this->pagedim[$trwsp['startpage']]['orm']) {
                                        $dl = $this->pagedim[$this->page]['orm'] - $this->pagedim[$trwsp['startpage']]['orm'];
                                        $rsstartx -= $dl;
                                        $rsendx -= $dl;
                                    } elseif (!$this->rtl and $this->pagedim[$this->page]['olm'] != $this->pagedim[$trwsp['startpage']]['olm']) {
                                        $dl = $this->pagedim[$this->page]['olm'] - $this->pagedim[$trwsp['startpage']]['olm'];
                                        $rsstartx += $dl;
                                        $rsendx += $dl;
                                    }
                                }
                                if ($trwsp['rowspan'] > 0 and $rsstartx > $this->x - $cellspacing['H'] - $current_cell_padding['L'] - $this->feps and $rsstartx < $this->x + $cellspacing['H'] + $current_cell_padding['R'] + $this->feps and ($trwsp['starty'] < $this->y - $this->feps or $trwsp['startpage'] < $this->page or $trwsp['startcolumn'] < $this->current_column)) {
                                    // set the starting X position of the current cell
                                    $this->x = $rsendx + $cellspacingx;
                                    // increment column indicator
                                    $colid += $trwsp['colspan'];
                                    if ($trwsp['rowspan'] == 1 and isset($dom[$trid]['endy']) and isset($dom[$trid]['endpage']) and isset($dom[$trid]['endcolumn']) and $trwsp['endpage'] == $dom[$trid]['endpage'] and $trwsp['endcolumn'] == $dom[$trid]['endcolumn']) {
                                        // set ending Y position for row
                                        $dom[$table_el]['rowspans'][$rsk]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
                                        $dom[$trid]['endy'] = $dom[$table_el]['rowspans'][$rsk]['endy'];
                                    }
                                    $rsk = 0;
                                } else {
                                    ++$rsk;
                                }
                            }
                        }
                        if (isset($dom[$parentid]['width'])) {
                            // user specified width
                            $cellw = $this->getHTMLUnitToUnits($dom[$parentid]['width'], $table_columns_width, 'px');
                            $tmpcw = $cellw / $colspan;
                            for ($i = 0; $i < $colspan; ++$i) {
                                $table_colwidths[$colid + $i] = $tmpcw;
                            }
                        } else {
                            // inherit column width
                            $cellw = 0;
                            for ($i = 0; $i < $colspan; ++$i) {
                                $cellw += $table_colwidths[$colid + $i];
                            }
                        }
                        $cellw += ($colspan - 1) * $cellspacing['H'];
                        // increment column indicator
                        $colid += $colspan;
                        // add rowspan information to table element
                        if ($rowspan > 1) {
                            $trsid = array_push($dom[$table_el]['rowspans'], array('trid' => $trid, 'rowspan' => $rowspan, 'mrowspan' => $rowspan, 'colspan' => $colspan, 'startpage' => $this->page, 'startcolumn' => $this->current_column, 'startx' => $this->x, 'starty' => $this->y));
                        }
                        $cellid = array_push($dom[$trid]['cellpos'], array('startx' => $this->x));
                        if ($rowspan > 1) {
                            $dom[$trid]['cellpos'][$cellid - 1]['rowspanid'] = $trsid - 1;
                        }
                        // push background colors
                        if (isset($dom[$parentid]['bgcolor']) and $dom[$parentid]['bgcolor'] !== false) {
                            $dom[$trid]['cellpos'][$cellid - 1]['bgcolor'] = $dom[$parentid]['bgcolor'];
                        }
                        // store border info
                        if (isset($tdborder) and !empty($tdborder)) {
                            $dom[$trid]['cellpos'][$cellid - 1]['border'] = $tdborder;
                        }
                        $prevLastH = $this->lasth;
                        // store some info for multicolumn mode
                        if ($this->rtl) {
                            $this->colxshift['x'] = $this->w - $this->x - $this->rMargin;
                        } else {
                            $this->colxshift['x'] = $this->x - $this->lMargin;
                        }
                        $this->colxshift['s'] = $cellspacing;
                        $this->colxshift['p'] = $current_cell_padding;
                        // ****** write the cell content ******
                        $this->MultiCell($cellw, $cellh, $cell_content, false, $lalign, false, 2, '', '', true, 0, true, true, 0, 'T', false);
                        // restore some values
                        $this->colxshift = array('x' => 0, 's' => array('H' => 0, 'V' => 0), 'p' => array('L' => 0, 'T' => 0, 'R' => 0, 'B' => 0));
                        $this->lasth = $prevLastH;
                        $this->cell_padding = $old_cell_padding;
                        $dom[$trid]['cellpos'][$cellid - 1]['endx'] = $this->x;
                        // update the end of row position
                        if ($rowspan <= 1) {
                            if (isset($dom[$trid]['endy'])) {
                                if ($this->page == $dom[$trid]['endpage'] and $this->current_column == $dom[$trid]['endcolumn']) {
                                    $dom[$trid]['endy'] = max($this->y, $dom[$trid]['endy']);
                                } elseif ($this->page > $dom[$trid]['endpage'] or $this->current_column > $dom[$trid]['endcolumn']) {
                                    $dom[$trid]['endy'] = $this->y;
                                }
                            } else {
                                $dom[$trid]['endy'] = $this->y;
                            }
                            if (isset($dom[$trid]['endpage'])) {
                                $dom[$trid]['endpage'] = max($this->page, $dom[$trid]['endpage']);
                            } else {
                                $dom[$trid]['endpage'] = $this->page;
                            }
                            if (isset($dom[$trid]['endcolumn'])) {
                                $dom[$trid]['endcolumn'] = max($this->current_column, $dom[$trid]['endcolumn']);
                            } else {
                                $dom[$trid]['endcolumn'] = $this->current_column;
                            }
                        } else {
                            // account for row-spanned cells
                            $dom[$table_el]['rowspans'][$trsid - 1]['endx'] = $this->x;
                            $dom[$table_el]['rowspans'][$trsid - 1]['endy'] = $this->y;
                            $dom[$table_el]['rowspans'][$trsid - 1]['endpage'] = $this->page;
                            $dom[$table_el]['rowspans'][$trsid - 1]['endcolumn'] = $this->current_column;
                        }
                        if (isset($dom[$table_el]['rowspans'])) {
                            // update endy and endpage on rowspanned cells
                            foreach ($dom[$table_el]['rowspans'] as $k => $trwsp) {
                                if ($trwsp['rowspan'] > 0) {
                                    if (isset($dom[$trid]['endpage'])) {
                                        if ($trwsp['endpage'] == $dom[$trid]['endpage'] and $trwsp['endcolumn'] == $dom[$trid]['endcolumn']) {
                                            $dom[$table_el]['rowspans'][$k]['endy'] = max($dom[$trid]['endy'], $trwsp['endy']);
                                        } elseif ($trwsp['endpage'] < $dom[$trid]['endpage'] or $trwsp['endcolumn'] < $dom[$trid]['endcolumn']) {
                                            $dom[$table_el]['rowspans'][$k]['endy'] = $dom[$trid]['endy'];
                                            $dom[$table_el]['rowspans'][$k]['endpage'] = $dom[$trid]['endpage'];
                                            $dom[$table_el]['rowspans'][$k]['endcolumn'] = $dom[$trid]['endcolumn'];
                                        } else {
                                            $dom[$trid]['endy'] = $this->pagedim[$dom[$trid]['endpage']]['hk'] - $this->pagedim[$dom[$trid]['endpage']]['bm'];
                                        }
                                    }
                                }
                            }
                        }
                        $this->x += $cellspacingx / 2;
                    } else {
                        // opening tag (or self-closing tag)
                        if (!isset($opentagpos)) {
                            if ($this->inxobj) {
                                // we are inside an XObject template
                                $opentagpos = strlen($this->xobjects[$this->xobjid]['outdata']);
                            } elseif (!$this->InFooter) {
                                if (isset($this->footerlen[$this->page])) {
                                    $this->footerpos[$this->page] = $this->pagelen[$this->page] - $this->footerlen[$this->page];
                                } else {
                                    $this->footerpos[$this->page] = $this->pagelen[$this->page];
                                }
                                $opentagpos = $this->footerpos[$this->page];
                            }
                        }
                        $dom = $this->openHTMLTagHandler($dom, $key, $cell);
                    }
                } else {
                    // closing tag
                    $prev_numpages = $this->numpages;
                    $old_bordermrk = $this->bordermrk[$this->page];
                    $dom = $this->closeHTMLTagHandler($dom, $key, $cell, $maxbottomliney);
                    if ($this->bordermrk[$this->page] > $old_bordermrk) {
                        $startlinepos += $this->bordermrk[$this->page] - $old_bordermrk;
                    }
                    if ($prev_numpages > $this->numpages) {
                        $startlinepage = $this->page;
                    }
                }
            } elseif (strlen($dom[$key]['value']) > 0) {
                // print list-item
                if (!TCPDF_STATIC::empty_string($this->lispacer) and $this->lispacer != '^') {
                    $this->SetFont($pfontname, $pfontstyle, $pfontsize);
                    $this->resetLastH();
                    $minstartliney = $this->y;
                    $maxbottomliney = $startliney + $this->FontSize * $this->cell_height_ratio;
                    $this->putHtmlListBullet($this->listnum, $this->lispacer, $pfontsize);
                    $this->SetFont($curfontname, $curfontstyle, $curfontsize);
                    $this->resetLastH();
                    if (is_numeric($pfontsize) and $pfontsize > 0 and is_numeric($curfontsize) and $curfontsize > 0 and $pfontsize != $curfontsize) {
                        $pfontascent = $this->getFontAscent($pfontname, $pfontstyle, $pfontsize);
                        $pfontdescent = $this->getFontDescent($pfontname, $pfontstyle, $pfontsize);
                        $this->y += (($pfontsize - $curfontsize) * $this->cell_height_ratio / $this->k + $pfontascent - $curfontascent - $pfontdescent + $curfontdescent) / 2;
                        $minstartliney = min($this->y, $minstartliney);
                        $maxbottomliney = max($this->y + $pfontsize * $this->cell_height_ratio / $this->k, $maxbottomliney);
                    }
                }
                // text
                $this->htmlvspace = 0;
                if (!$this->premode and $this->isRTLTextDir()) {
                    // reverse spaces order
                    $lsp = '';
                    // left spaces
                    $rsp = '';
                    // right spaces
                    if (preg_match('/^(' . $this->re_space['p'] . '+)/' . $this->re_space['m'], $dom[$key]['value'], $matches)) {
                        $lsp = $matches[1];
                    }
                    if (preg_match('/(' . $this->re_space['p'] . '+)$/' . $this->re_space['m'], $dom[$key]['value'], $matches)) {
                        $rsp = $matches[1];
                    }
                    $dom[$key]['value'] = $rsp . $this->stringTrim($dom[$key]['value']) . $lsp;
                }
                if ($newline) {
                    if (!$this->premode) {
                        $prelen = strlen($dom[$key]['value']);
                        if ($this->isRTLTextDir()) {
                            // right trim except non-breaking space
                            $dom[$key]['value'] = $this->stringRightTrim($dom[$key]['value']);
                        } else {
                            // left trim except non-breaking space
                            $dom[$key]['value'] = $this->stringLeftTrim($dom[$key]['value']);
                        }
                        $postlen = strlen($dom[$key]['value']);
                        if ($postlen == 0 and $prelen > 0) {
                            $dom[$key]['trimmed_space'] = true;
                        }
                    }
                    $newline = false;
                    $firstblock = true;
                } else {
                    $firstblock = false;
                    // replace empty multiple spaces string with a single space
                    $dom[$key]['value'] = preg_replace('/^' . $this->re_space['p'] . '+$/' . $this->re_space['m'], chr(32), $dom[$key]['value']);
                }
                $strrest = '';
                if ($this->rtl) {
                    $this->x -= $this->textindent;
                } else {
                    $this->x += $this->textindent;
                }
                if (!isset($dom[$key]['trimmed_space']) or !$dom[$key]['trimmed_space']) {
                    $strlinelen = $this->GetStringWidth($dom[$key]['value']);
                    if (!empty($this->HREF) and isset($this->HREF['url'])) {
                        // HTML <a> Link
                        $hrefcolor = '';
                        if (isset($dom[$dom[$key]['parent']]['fgcolor']) and $dom[$dom[$key]['parent']]['fgcolor'] !== false) {
                            $hrefcolor = $dom[$dom[$key]['parent']]['fgcolor'];
                        }
                        $hrefstyle = -1;
                        if (isset($dom[$dom[$key]['parent']]['fontstyle']) and $dom[$dom[$key]['parent']]['fontstyle'] !== false) {
                            $hrefstyle = $dom[$dom[$key]['parent']]['fontstyle'];
                        }
                        $strrest = $this->addHtmlLink($this->HREF['url'], $dom[$key]['value'], $wfill, true, $hrefcolor, $hrefstyle, true);
                    } else {
                        $wadj = 0;
                        // space to leave for block continuity
                        if ($this->rtl) {
                            $cwa = $this->x - $this->lMargin;
                        } else {
                            $cwa = $this->w - $this->rMargin - $this->x;
                        }
                        if ($strlinelen < $cwa and isset($dom[$key + 1]) and $dom[$key + 1]['tag'] and !$dom[$key + 1]['block']) {
                            // check the next text blocks for continuity
                            $nkey = $key + 1;
                            $write_block = true;
                            $same_textdir = true;
                            $tmp_fontname = $this->FontFamily;
                            $tmp_fontstyle = $this->FontStyle;
                            $tmp_fontsize = $this->FontSizePt;
                            while ($write_block and isset($dom[$nkey])) {
                                if ($dom[$nkey]['tag']) {
                                    if ($dom[$nkey]['block']) {
                                        // end of block
                                        $write_block = false;
                                    }
                                    $tmp_fontname = isset($dom[$nkey]['fontname']) ? $dom[$nkey]['fontname'] : $this->FontFamily;
                                    $tmp_fontstyle = isset($dom[$nkey]['fontstyle']) ? $dom[$nkey]['fontstyle'] : $this->FontStyle;
                                    $tmp_fontsize = isset($dom[$nkey]['fontsize']) ? $dom[$nkey]['fontsize'] : $this->FontSizePt;
                                    $same_textdir = $dom[$nkey]['dir'] == $dom[$key]['dir'];
                                } else {
                                    $nextstr = TCPDF_STATIC::pregSplit('/' . $this->re_space['p'] . '+/', $this->re_space['m'], $dom[$nkey]['value']);
                                    if (isset($nextstr[0]) and $same_textdir) {
                                        $wadj += $this->GetStringWidth($nextstr[0], $tmp_fontname, $tmp_fontstyle, $tmp_fontsize);
                                        if (isset($nextstr[1])) {
                                            $write_block = false;
                                        }
                                    }
                                }
                                ++$nkey;
                            }
                        }
                        if ($wadj > 0 and $strlinelen + $wadj >= $cwa) {
                            $wadj = 0;
                            $nextstr = TCPDF_STATIC::pregSplit('/' . $this->re_space['p'] . '/', $this->re_space['m'], $dom[$key]['value']);
                            $numblks = count($nextstr);
                            if ($numblks > 1) {
                                // try to split on blank spaces
                                $wadj = $cwa - $strlinelen + $this->GetStringWidth($nextstr[$numblks - 1]);
                            } else {
                                // set the entire block on new line
                                $wadj = $this->GetStringWidth($nextstr[0]);
                            }
                        }
                        // check for reversed text direction
                        if ($wadj > 0 and ($this->rtl and $this->tmprtl === 'L' or !$this->rtl and $this->tmprtl === 'R')) {
                            // LTR text on RTL direction or RTL text on LTR direction
                            $reverse_dir = true;
                            $this->rtl = !$this->rtl;
                            $revshift = $strlinelen + $wadj + 1.0E-6;
                            // add little quantity for rounding problems
                            if ($this->rtl) {
                                $this->x += $revshift;
                            } else {
                                $this->x -= $revshift;
                            }
                            $xws = $this->x;
                        }
                        // ****** write only until the end of the line and get the rest ******
                        $strrest = $this->Write($this->lasth, $dom[$key]['value'], '', $wfill, '', false, 0, true, $firstblock, 0, $wadj);
                        // restore default direction
                        if ($reverse_dir and $wadj == 0) {
                            $this->x = $xws;
                            $this->rtl = !$this->rtl;
                            $reverse_dir = false;
                        }
                    }
                }
                $this->textindent = 0;
                if (strlen($strrest) > 0) {
                    // store the remaining string on the previous $key position
                    $this->newline = true;
                    if ($strrest == $dom[$key]['value']) {
                        // used to avoid infinite loop
                        ++$loop;
                    } else {
                        $loop = 0;
                    }
                    $dom[$key]['value'] = $strrest;
                    if ($cell) {
                        if ($this->rtl) {
                            $this->x -= $this->cell_padding['R'];
                        } else {
                            $this->x += $this->cell_padding['L'];
                        }
                    }
                    if ($loop < 3) {
                        --$key;
                    }
                } else {
                    $loop = 0;
                    // add the positive font spacing of the last character (if any)
                    if ($this->font_spacing > 0) {
                        if ($this->rtl) {
                            $this->x -= $this->font_spacing;
                        } else {
                            $this->x += $this->font_spacing;
                        }
                    }
                }
            }
            ++$key;
            if (isset($dom[$key]['tag']) and $dom[$key]['tag'] and (!isset($dom[$key]['opening']) or !$dom[$key]['opening']) and isset($dom[$dom[$key]['parent']]['attribute']['nobr']) and $dom[$dom[$key]['parent']]['attribute']['nobr'] == 'true') {
                // check if we are on a new page or on a new column
                if (!$undo and ($this->y < $this->start_transaction_y or $dom[$key]['value'] == 'tr' and $dom[$dom[$key]['parent']]['endy'] < $this->start_transaction_y)) {
                    // we are on a new page or on a new column and the total object height is less than the available vertical space.
                    // restore previous object
                    $this->rollbackTransaction(true);
                    // restore previous values
                    foreach ($this_method_vars as $vkey => $vval) {
                        ${$vkey} = $vval;
                    }
                    // add a page (or trig AcceptPageBreak() for multicolumn mode)
                    $pre_y = $this->y;
                    if (!$this->checkPageBreak($this->PageBreakTrigger + 1) and $this->y < $pre_y) {
                        $startliney = $this->y;
                    }
                    $undo = true;
                    // avoid infinite loop
                } else {
                    $undo = false;
                }
            }
        }
        // end for each $key
        // align the last line
        if (isset($startlinex)) {
            $yshift = $minstartliney - $startliney;
            if ($yshift > 0 or $this->page > $startlinepage) {
                $yshift = 0;
            }
            $t_x = 0;
            // the last line must be shifted to be aligned as requested
            $linew = abs($this->endlinex - $startlinex);
            if ($this->inxobj) {
                // we are inside an XObject template
                $pstart = substr($this->xobjects[$this->xobjid]['outdata'], 0, $startlinepos);
                if (isset($opentagpos)) {
                    $midpos = $opentagpos;
                } else {
                    $midpos = 0;
                }
                if ($midpos > 0) {
                    $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos, $midpos - $startlinepos);
                    $pend = substr($this->xobjects[$this->xobjid]['outdata'], $midpos);
                } else {
                    $pmid = substr($this->xobjects[$this->xobjid]['outdata'], $startlinepos);
                    $pend = '';
                }
            } else {
                $pstart = substr($this->getPageBuffer($startlinepage), 0, $startlinepos);
                if (isset($opentagpos) and isset($this->footerlen[$startlinepage]) and !$this->InFooter) {
                    $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
                    $midpos = min($opentagpos, $this->footerpos[$startlinepage]);
                } elseif (isset($opentagpos)) {
                    $midpos = $opentagpos;
                } elseif (isset($this->footerlen[$startlinepage]) and !$this->InFooter) {
                    $this->footerpos[$startlinepage] = $this->pagelen[$startlinepage] - $this->footerlen[$startlinepage];
                    $midpos = $this->footerpos[$startlinepage];
                } else {
                    $midpos = 0;
                }
                if ($midpos > 0) {
                    $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos, $midpos - $startlinepos);
                    $pend = substr($this->getPageBuffer($startlinepage), $midpos);
                } else {
                    $pmid = substr($this->getPageBuffer($startlinepage), $startlinepos);
                    $pend = '';
                }
            }
            if (isset($plalign) and ($plalign == 'C' or $plalign == 'R' and !$this->rtl or $plalign == 'L' and $this->rtl)) {
                // calculate shifting amount
                $tw = $w;
                if ($this->lMargin != $prevlMargin) {
                    $tw += $prevlMargin - $this->lMargin;
                }
                if ($this->rMargin != $prevrMargin) {
                    $tw += $prevrMargin - $this->rMargin;
                }
                $one_space_width = $this->GetStringWidth(chr(32));
                $no = 0;
                // number of spaces on a line contained on a single block
                if ($this->isRTLTextDir()) {
                    // RTL
                    // remove left space if exist
                    $pos1 = TCPDF_STATIC::revstrpos($pmid, '[(');
                    if ($pos1 > 0) {
                        $pos1 = intval($pos1);
                        if ($this->isUnicodeFont()) {
                            $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[(' . chr(0) . chr(32)));
                            $spacelen = 2;
                        } else {
                            $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, '[(' . chr(32)));
                            $spacelen = 1;
                        }
                        if ($pos1 == $pos2) {
                            $pmid = substr($pmid, 0, $pos1 + 2) . substr($pmid, $pos1 + 2 + $spacelen);
                            if (substr($pmid, $pos1, 4) == '[()]') {
                                $linew -= $one_space_width;
                            } elseif ($pos1 == strpos($pmid, '[(')) {
                                $no = 1;
                            }
                        }
                    }
                } else {
                    // LTR
                    // remove right space if exist
                    $pos1 = TCPDF_STATIC::revstrpos($pmid, ')]');
                    if ($pos1 > 0) {
                        $pos1 = intval($pos1);
                        if ($this->isUnicodeFont()) {
                            $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(0) . chr(32) . ')]')) + 2;
                            $spacelen = 2;
                        } else {
                            $pos2 = intval(TCPDF_STATIC::revstrpos($pmid, chr(32) . ')]')) + 1;
                            $spacelen = 1;
                        }
                        if ($pos1 == $pos2) {
                            $pmid = substr($pmid, 0, $pos1 - $spacelen) . substr($pmid, $pos1);
                            $linew -= $one_space_width;
                        }
                    }
                }
                $mdiff = $tw - $linew;
                if ($plalign == 'C') {
                    if ($this->rtl) {
                        $t_x = -($mdiff / 2);
                    } else {
                        $t_x = $mdiff / 2;
                    }
                } elseif ($plalign == 'R') {
                    // right alignment on LTR document
                    $t_x = $mdiff;
                } elseif ($plalign == 'L') {
                    // left alignment on RTL document
                    $t_x = -$mdiff;
                }
            }
            // end if startlinex
            if ($t_x != 0 or $yshift < 0) {
                // shift the line
                $trx = sprintf('1 0 0 1 %F %F cm', $t_x * $this->k, $yshift * $this->k);
                $pstart .= "\nq\n" . $trx . "\n" . $pmid . "\nQ\n";
                $endlinepos = strlen($pstart);
                if ($this->inxobj) {
                    // we are inside an XObject template
                    $this->xobjects[$this->xobjid]['outdata'] = $pstart . $pend;
                    foreach ($this->xobjects[$this->xobjid]['annotations'] as $pak => $pac) {
                        if ($pak >= $pask) {
                            $this->xobjects[$this->xobjid]['annotations'][$pak]['x'] += $t_x;
                            $this->xobjects[$this->xobjid]['annotations'][$pak]['y'] -= $yshift;
                        }
                    }
                } else {
                    $this->setPageBuffer($startlinepage, $pstart . $pend);
                    // shift the annotations and links
                    if (isset($this->PageAnnots[$this->page])) {
                        foreach ($this->PageAnnots[$this->page] as $pak => $pac) {
                            if ($pak >= $pask) {
                                $this->PageAnnots[$this->page][$pak]['x'] += $t_x;
                                $this->PageAnnots[$this->page][$pak]['y'] -= $yshift;
                            }
                        }
                    }
                }
                $this->y -= $yshift;
                $yshift = 0;
            }
        }
        // restore previous values
        $this->setGraphicVars($gvars);
        if ($this->num_columns > 1) {
            $this->selectColumn();
        } elseif ($this->page > $prevPage) {
            $this->lMargin = $this->pagedim[$this->page]['olm'];
            $this->rMargin = $this->pagedim[$this->page]['orm'];
        }
        // restore previous list state
        $this->cell_height_ratio = $prev_cell_height_ratio;
        $this->listnum = $prev_listnum;
        $this->listordered = $prev_listordered;
        $this->listcount = $prev_listcount;
        $this->lispacer = $prev_lispacer;
        if ($ln and !($cell and $dom[$key - 1]['value'] == 'table')) {
            $this->Ln($this->lasth);
            if ($this->y < $maxbottomliney) {
                $this->y = $maxbottomliney;
            }
        }
        unset($dom);
    }
	/**
	 * Converts UTF-8 strings to codepoints array.<br>
	 * Invalid byte sequences will be replaced with 0xFFFD (replacement character)<br>
	 * @param $str (string) string to process.
	 * @param $isunicode (boolean) True when the documetn is in Unicode mode, false otherwise.
	 * @param $currentfont (array) Reference to current font array.
	 * @return array containing codepoints (UTF-8 characters values)
	 * @author Nicola Asuni
	 * @public static
	 */
	public static function UTF8StringToArray($str, $isunicode=true, &$currentfont) {
		if ($isunicode) {
			// requires PCRE unicode support turned on
			$chars = TCPDF_STATIC::pregSplit('//','u', $str, -1, PREG_SPLIT_NO_EMPTY);
			$carr = array_map(array('TCPDF_FONTS', 'uniord'), $chars);
		} else {
			$chars = str_split($str);
			$carr = array_map('ord', $chars);
		}
		$currentfont['subsetchars'] += array_fill_keys($carr, true);
		return $carr;
	}