Php 使用UFPDF的FPDF Unicode支持

Php 使用UFPDF的FPDF Unicode支持,php,unicode,fpdf,Php,Unicode,Fpdf,我已经为此绞尽脑汁很久了,我怀疑其他用户也会这么做 首先我必须说,我没有FPDF的替代品,因为我使用了很多其他FPDF模块,所以请尽量不要建议使用其他库,如TCPDF 我真的需要使FPDF能够以稳定的方式处理UTF-8字符 我已经发现的: 有一个扩展名为UFPDF 该扩展目前只支持TrueType字体,但它应该适合我。 .ttf文件必须由一个名为ttf2ufm的工具进行转换,并使用给定的工具makefontuni.php将生成的.ufm和source.ttf转换为font.php、font.z

我已经为此绞尽脑汁很久了,我怀疑其他用户也会这么做

首先我必须说,我没有FPDF的替代品,因为我使用了很多其他FPDF模块,所以请尽量不要建议使用其他库,如TCPDF

我真的需要使FPDF能够以稳定的方式处理UTF-8字符

我已经发现的:

有一个扩展名为UFPDF

该扩展目前只支持TrueType字体,但它应该适合我。 .ttf文件必须由一个名为ttf2ufm的工具进行转换,并使用给定的工具makefontuni.php将生成的.ufm和source.ttf转换为font.php、font.z和font.ctg.z文件

到目前为止还不错。所以我试着从我的电脑转换成Arial字体。(arial.ttf、arialbd.ttf、arialbi.ttf、ariali.ttf)

它成功了,我能够生成一个带有unicode字符的test.pdf。但是这是一个由AdobeReader显示的错误弹出窗口,上面写着:Bad参数-字体ArialMT包含Bad/Widths

我注意到所有字符都有相同的宽度(我怀疑是默认宽度),所以我尝试调试

我发现UPDF将宽度添加到PDF中,如下所示:

charnumber [width] charnumber [width]

85 [276] (for the "u" character)
U -70 ; WX 450 ; N uni06BE ; G 1003 ; B -70 256 788 1136 ;
function charlength($char) {    
    $cw = &$this->CurrentFont['cw'];
    $utf8dec = $this->ordutf8($char, $offset);        
    if(!isset($cw[$utf8dec])) {
        return 0;
    }
    return $cw[$utf8dec];
}


function ordutf8($string, &$offset) {
    $string = class_stringTools::utf8_decode($string);
    $code = ord(substr($string, $offset,1));
    if ($code >= 128) {        //otherwise 0xxxxxxx
        if ($code < 224) $bytesnumber = 2;                //110xxxxx
        else if ($code < 240) $bytesnumber = 3;        //1110xxxx
        else if ($code < 248) $bytesnumber = 4;    //11110xxx
        else return -1;
        $codetemp = $code - 192 - ($bytesnumber > 2 ? 32 : 0) - ($bytesnumber > 3 ? 16 : 0);
        for ($i = 2; $i <= $bytesnumber; $i++) {
            $offset ++;
            $code2 = ord(substr($string, $offset, 1)) - 128;        //10xxxxxx
            $codetemp = $codetemp*64 + $code2;
        }
        $code = $codetemp;
    }
    $offset += 1;
    if ($offset >= strlen($string)) $offset = -1;
    return $code;
}
我发现有些字符的索引值为负值:

-70 [266]
索引值由ttf2ufm创建。如果我查看结果arial.ufm,我发现如下条目:

charnumber [width] charnumber [width]

85 [276] (for the "u" character)
U -70 ; WX 450 ; N uni06BE ; G 1003 ; B -70 256 788 1136 ;
function charlength($char) {    
    $cw = &$this->CurrentFont['cw'];
    $utf8dec = $this->ordutf8($char, $offset);        
    if(!isset($cw[$utf8dec])) {
        return 0;
    }
    return $cw[$utf8dec];
}


function ordutf8($string, &$offset) {
    $string = class_stringTools::utf8_decode($string);
    $code = ord(substr($string, $offset,1));
    if ($code >= 128) {        //otherwise 0xxxxxxx
        if ($code < 224) $bytesnumber = 2;                //110xxxxx
        else if ($code < 240) $bytesnumber = 3;        //1110xxxx
        else if ($code < 248) $bytesnumber = 4;    //11110xxx
        else return -1;
        $codetemp = $code - 192 - ($bytesnumber > 2 ? 32 : 0) - ($bytesnumber > 3 ? 16 : 0);
        for ($i = 2; $i <= $bytesnumber; $i++) {
            $offset ++;
            $code2 = ord(substr($string, $offset, 1)) - 128;        //10xxxxxx
            $codetemp = $codetemp*64 + $code2;
        }
        $code = $codetemp;
    }
    $offset += 1;
    if ($offset >= strlen($string)) $offset = -1;
    return $code;
}
我怀疑U是utf-8表中的索引,我修改了makefontuni.php,使其忽略U的负值。再次创建了font.php、font.z和font.ctg.z,它成功了。错误通知未显示,字符显示的宽度正确

所以第一个问题是: 为什么ttf2ufm为U产生负值?这是正确的吗?如果它是正确的,为什么AdobeReader不能处理它

我希望这是全部,但事实并非如此

我使用粗体字体做了一些测试,当使用arial粗体时,较低的“u”字符显示为一个奇怪的符号

我再次调试,在arialbd.ufm中找到了“u”字符的这一行

U 117 ; WX 611 ; N u ; G 88 ; B 141 -24 1107 1062 ;
我在那个文件中搜索了“U 117”,发现了另一个以“U 117;”开头的字符。我已经删除了它,所以我不能在这里发布该行。但是,这是pdf中显示的错误字符,删除后,u已正确显示

那么第二个问题是:ttf2ufm生成具有相同索引的两个字符的.ufm文件的原因是什么?这种情况只发生在arialbd.ttf上,而不发生在arial.ttf上

但是我现在解决了它,希望没有其他双索引字符

更多问题:

我发现生成的arial.php包含字符宽度:

$cw=array(
    32=>278, 160=>278, 33=>278, 34=>355, 35=>556, 36=>556, 
    37=>889, 38=>667, 39=>191, 40=>333, 41=>333, 42=>389, 43=>584, 
    44=>278, 45=>333, 173=>333, [...]
非unicode版本的arial.php也包含
$cw
数组。但它使用字符本身作为索引,而不是索引号:

$cw=array(  
    chr(0)=>750,chr(1)=>750,chr(2)=>750,chr(3)=>750,chr(4)=>750,
    chr(5)=>750,chr(6)=>750,chr(7)=>750,chr(8)=>750,chr(9)=>750,chr(10)=>750,
    chr(11)=>750,chr(12)=>750, [...]
php有时会尝试访问
$cw
值,其他一些模块也会这样做,以便能够计算给定字符串的宽度。对于UFPDF,所有这些都失败了

我试图通过修改fpdf.php和所有试图访问
$cw
的模块来修复它,如下所示:

charnumber [width] charnumber [width]

85 [276] (for the "u" character)
U -70 ; WX 450 ; N uni06BE ; G 1003 ; B -70 256 788 1136 ;
function charlength($char) {    
    $cw = &$this->CurrentFont['cw'];
    $utf8dec = $this->ordutf8($char, $offset);        
    if(!isset($cw[$utf8dec])) {
        return 0;
    }
    return $cw[$utf8dec];
}


function ordutf8($string, &$offset) {
    $string = class_stringTools::utf8_decode($string);
    $code = ord(substr($string, $offset,1));
    if ($code >= 128) {        //otherwise 0xxxxxxx
        if ($code < 224) $bytesnumber = 2;                //110xxxxx
        else if ($code < 240) $bytesnumber = 3;        //1110xxxx
        else if ($code < 248) $bytesnumber = 4;    //11110xxx
        else return -1;
        $codetemp = $code - 192 - ($bytesnumber > 2 ? 32 : 0) - ($bytesnumber > 3 ? 16 : 0);
        for ($i = 2; $i <= $bytesnumber; $i++) {
            $offset ++;
            $code2 = ord(substr($string, $offset, 1)) - 128;        //10xxxxxx
            $codetemp = $codetemp*64 + $code2;
        }
        $code = $codetemp;
    }
    $offset += 1;
    if ($offset >= strlen($string)) $offset = -1;
    return $code;
}
我在fpdf类中创建了一个名为
charlength
的方法:

function charlength($char) 
{
    $cw = &$this->CurrentFont['cw'];
    return $cw[$char];
}
并使FPDF在需要访问
$this->CurrentFont['cw']
时调用
charlength

function GetStringWidth($s)
{
    // Get width of a string in the current font
    $s = (string)$s;
    // $cw = &$this->CurrentFont['cw']; // Old FPDF-Code
    $w = 0;
    $l = strlen($s);
    for($i=0;$i<$l;$i++) {
        // $w += $cw[$s[$i]]; // Old FPDF-Code
        $w += $this->charlength($s[$i]); // My replacement
    }
    return $w*$this->FontSize/1000;
}
ordutf8
方法来自php.net,但我不得不修改它,因为有一次
$code
的值为252,导致未定义的
$bytenumber

不过,它现在似乎可以工作,但我对编辑fpdf.php的源代码和其他模块的源代码不太满意。我想知道没有其他人报告我所遇到的问题

我知道我写了很多,但我想知道是否每个人都有同样的问题。你觉得最后的修改怎么样?你有什么改进吗?我真的需要一个稳定的方法使FPDF支持unicode字符。请帮帮我

令人遗憾的是,ufpdf的作者没有时间支持这一点。

您也检查了吗?它支持UTF-8和字体子集设置。适用于从左到右的语言。