如題:如何計算字串"寬"度
是【寬】不是【長】唷~
問題描述:
我有一個字串,例如:$text = "測試資訊information!";
我要如何取得$text的真實寬度呢?
PS1:使用mb_strlen($text,"utf-8")
;可以取得長度,會得到16
PS2:因中文字的字體方正特性,所以中文字的字體寬度都是固定的,如果只是純中文字串,我可以預設大約的寬度(例如10px),再利用PS1的方法相乘計算。但是,英文字體的寬都不一樣,例如m與i的寬度就不一致,該怎麼計算呢?
PS3:font-family有個Courier的等寬字體可以使用,但是因為某種原因,無法設定為這個字型...=..=
以字母/數字寬度來分類, 英文字型有兩種: 等寬monospaced和比例proportional.不同字型的寬度也可能不同.
如果您使用TTF字型, 也許您可以試試 PHP的函式:
http://www.php.net/imagettfbbox
這個方法我也有查到~
可是就如同我PS3說的..
因為某種原因無法設定字型
那個原因就是我用TCPDF將傳給我的HTML字串轉成PDF檔..
而TCPDF字體的設定是$pdf->setFont('cid0ct', '', 14);
其中..cid0ct是TCPDF內建字型
檔案是PHP不是ttf檔
那這個呢?
http://stackoverflow.com/questions/25208064/tcpdf-how-to-use-getstringwidth-when-text-is-bold
原始碼:
/**
* Returns the length of a string in user unit. A font must be selected.<br>
* @param $s (string) The string whose length is to be computed
* @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
* @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line-through</li><li>O: overline</li></ul> or any combination. The default value is regular.
* @param $fontsize (float) Font size in points. The default value is the current size.
* @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
* @return mixed int total string length or array of characted widths
* @author Nicola Asuni
* @public
* @since 1.2
*/
public function GetStringWidth($s, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
return $this->GetArrStringWidth(TCPDF_FONTS::utf8Bidi(TCPDF_FONTS::UTF8StringToArray($s, $this->isunicode, $this->CurrentFont), $s, $this->tmprtl, $this->isunicode, $this->CurrentFont), $fontname, $fontstyle, $fontsize, $getarray);
}
剛剛試完~確認可以使用~
不過還有兩個問題待解決~
getStringWidth呼叫getArrStringWidth:
/**
* Returns the string length of an array of chars in user unit or an array of characters widths. A font must be selected.<br>
* @param $sa (string) The array of chars whose total length is to be computed
* @param $fontname (string) Family font. It can be either a name defined by AddFont() or one of the standard families. It is also possible to pass an empty string, in that case, the current family is retained.
* @param $fontstyle (string) Font style. Possible values are (case insensitive):<ul><li>empty string: regular</li><li>B: bold</li><li>I: italic</li><li>U: underline</li><li>D: line through</li><li>O: overline</li></ul> or any combination. The default value is regular.
* @param $fontsize (float) Font size in points. The default value is the current size.
* @param $getarray (boolean) if true returns an array of characters widths, if false returns the total length.
* @return mixed int total string length or array of characted widths
* @author Nicola Asuni
* @public
* @since 2.4.000 (2008-03-06)
*/
public function GetArrStringWidth($sa, $fontname='', $fontstyle='', $fontsize=0, $getarray=false) {
// store current values
if (!TCPDF_STATIC::empty_string($fontname)) {
$prev_FontFamily = $this->FontFamily;
$prev_FontStyle = $this->FontStyle;
$prev_FontSizePt = $this->FontSizePt;
$this->SetFont($fontname, $fontstyle, $fontsize, '', 'default', false);
}
// convert UTF-8 array to Latin1 if required
if ($this->isunicode AND (!$this->isUnicodeFont())) {
$sa = TCPDF_FONTS::UTF8ArrToLatin1Arr($sa);
}
$w = 0; // total width
$wa = array(); // array of characters widths
foreach ($sa as $ck => $char) {
// character width
$cw = $this->GetCharWidth($char, isset($sa[($ck + 1)]));
$wa[] = $cw;
$w += $cw;
}
// restore previous values
if (!TCPDF_STATIC::empty_string($fontname)) {
$this->SetFont($prev_FontFamily, $prev_FontStyle, $prev_FontSizePt, '', 'default', false);
}
if ($getarray) {
return $wa;
}
return $w;
}
其中, $getarray可做為除錯用, 看看個別字元的寬度. 另外, 您可以在此函數中嵌進除錯碼, 看看發生什麼事. 或使用除錯器看看.
getArrStringWidth呼叫getCharWidth
/**
* Returns the length of the char in user unit for the current font considering current stretching and spacing (tracking).
* @param $char (int) The char code whose length is to be returned
* @param $notlast (boolean) If false ignore the font-spacing.
* @return float char width
* @author Nicola Asuni
* @public
* @since 2.4.000 (2008-03-06)
*/
public function GetCharWidth($char, $notlast=true) {
// get raw width
$chw = $this->getRawCharWidth($char);
if (($this->font_spacing < 0) OR (($this->font_spacing > 0) AND $notlast)) {
// increase/decrease font spacing
$chw += $this->font_spacing;
}
if ($this->font_stretching != 100) {
// fixed stretching mode
$chw *= ($this->font_stretching / 100);
}
return $chw;
}
建議您追蹤TCPDF的原始碼去檢查怎麼計算各寬度的.
老大!!
我找到function不能執行GetStringWidth的原因了~
原本是這麼做的..
function test($text) {
$textwidth = $pdf->GetStringWidth('Text Test', 'cid0ct', '', 14);
//...以下省略...
}
我猜會不會是$pdf的關係,於是我這麼做..
function test($text) {
$pdf = new MYPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
$textwidth = $pdf->GetStringWidth('Text Test', 'cid0ct', '', 14);
//...以下省略...
}
雖然我function外層也有同一行在做定義$pdf
但好像到function就必須重新定義一次
不過我有個問題...
因為每個表格都會跑一次function
每次都new一個新的...會不會有問題~
(雖然function執行完後這個內部$pdf會不見..)
這個tctable提供您參考:
https://github.com/voilab/tctable
用$this->$pdf->
function test($text) {
$textwidth = $this->$pdf->GetStringWidth('Text Test', 'cid0ct', '', 14);
//...以下省略...
}
解決了~
我覺得這是個很難的問題
但是如果你都可以做出「每個中文字都是10px」這種假設的話
那麼問題就可以簡化成
「解析出每個字元是中文或英文,然後累計該字元的寬度」
程式大略如下
可以的話就改去用
<?php
$string = "中go英od 文 jo交b!錯";
$len = my_strlen($string);
echo ($string . " length = [$len]\n");
function my_strlen($string) {
$arrLength = array(
"!" => 5, " " => 7,
"a" => 8, "b" => 8, "c" => 8,
"d" => 8, "e" => 8, "f" => 8,
"g" => 8, "h" => 8, "i" => 8,
"j" => 7, "k" => 8, "l" => 8,
"m" => 8, "n" => 8, "o" => 8,
"p" => 8, "q" => 8, "r" => 8, "s" => 8,
"t" => 8, "u" => 8, "v" => 8,
"w" => 8, "x" => 8, "y" => 8, "z" => 8
);
$iReturn = 0;
$smblen = mb_strlen($string, 'UTF-8');
for ($i = 0; $i < $smblen; $i++) {
$char = mb_substr($string, $i, 1, 'UTF-8');
if (array_key_exists($char, $arrLength)) {
$charLen = $arrLength[$char];
} else {
$charLen = 10; //中文字寬度
}
echo ("[$i]=[$char] Len=[$charLen]\n");
$iReturn = $iReturn + $charLen;
}
return $iReturn;
}
?>
這個方法我有想過~
但因為我要得到寬度的內容很多(至少有40幾個)
而其中有大約10幾個內容會有兩三百個字元(含中文字)
這個方法是讓每個字都去跟陣列配對屬於自己的寬度..
這..loading...應該會很重...
尤其我公司案子是醫院..
醫生處理病人資料要快..
所以如果變慢..應該會被醫生洗臉~XD
目前樓上 bizpro 大大提供的函式最適用
回傳的寬度數值也挺精確的~
不過..我在其下方有留言..還有兩個問題待解決~
一起集思廣益吧~哈哈~XD
其他的我不敢確定
但我可以確定 TCPDF 做的事比我多得多多了
GetArrStringWidth 呼叫 GetCharWidth
GetCharWidth 呼叫 getRawCharWidth
getRawCharWidth 呼叫 getAbsFontMeasure
getAbsFontMeasure 回傳 measure
如果你認為讓每個字都去跟陣列配對屬於自己的寬度..
會讓程式變慢的話
再讓你看一段 TCPDF 的 source code
你看看他是在做什麼
/**
* Returns the length of the char in user unit for the current font.
* @param $char (int) The char code whose length is to be returned
* @return float char width
* @author Nicola Asuni
* @public
* @since 5.9.000 (2010-09-28)
*/
public function getRawCharWidth($char) {
if ($char == 173) {
// SHY character will not be printed
return (0);
}
if (isset($this->CurrentFont['cw'][$char])) {
$w = $this->CurrentFont['cw'][$char];
} elseif (isset($this->CurrentFont['dw'])) {
// default width
$w = $this->CurrentFont['dw'];
} elseif (isset($this->CurrentFont['cw'][32])) {
// default width
$w = $this->CurrentFont['cw'][32];
} else {
$w = 600;
}
return $this->getAbsFontMeasure($w);
}
最後
我只想說
Good luck.
其實後來又回頭看原始碼...
確實做得事情比你的多...
我的錯~抱歉~~
是我想多了~XD
不過~換個角度想~至少我可以不用自己定義每個字元的寬度~
自己定義又不一定準確~
而且~也不一定全部都定義得到~例如一些特殊符號~
最後
我只想說
Thanks for your answer.
沒在用PHP的我,查了這個 imagefontwidth
http://php.net/manual/en/function.imagefontwidth.php
那麼可以把字串長度中的每個字分別抓出字寬相加
即可得到所說的 字串"寬"度