在php中,截取部份字元可以用
mb_substr($prodcname,0,20,"UTF-8")
或
substr($prodcname,0,20)
在印報表時,假設某個欄位可以放中文 20 字或英文 40 個字,在純中文字或純英文字時,可以決定使用那個函數,但遇到中英夾雜時就很麻煩了。
當使用 mb_substr($prodcname,0,20,"UTF-8"),遇到全英文時,該欄只印了一半,空了一大塊。
若使用 substr($prodcname,0,20)時,碰到中文時又會印出格子外。
有沒有兩全其美的方法可以充分利用欄寛限制?儘量印好印滿!
/**
* 依據寬度擷取字串
*
* @param string $str 原始 utf8 字串
* @param int $width 寬度,unocide<128時寬度為1,其餘為2
* @return string 不大於 $width 的最長子字串
*/
function getSubStringByWidth($str, $width) {
$n=strlen($str);
$k=0;
while($k<$n) {
$c=ord($str[$k++]);
if($c<128) {
$r=0; //後面還要讀取幾個 byte
$len=1; //這個字的寬度
} elseif(($c&0xe0)===0xc0) {
$r=1;
$len=2;
} elseif(($c&0xf0)===0xe0) {
$r=2;
$len=2;
} elseif(($c&0xf8)===0xf0) {
$r=3;
$len=2;
} else {
throw new \Exception('bad utf8 encoding');
}
if($len>$width) {
--$k;
break;
}
$width-=$len;
//後面要接 r個 10xx xxxx
for(;$r>0 && $k<$n;--$r) {
$c=ord($str[$k++]);
if(($c&0xc0)!==0x80) {
break;
}
}
if($r>0) {
throw new \Exception('bad utf8 encoding');
}
}
return substr($str, 0, $k);
}
參考:
以下是我以前自已寫的,給你參考使用。
不過我這寫法遇到起始或截斷的地方剛好是中文。有機會會多一格。
如果你想少一格的話。就將「//取得長度足夠時,跳出」下面那斷判斷式移到「//是否超過起始位置」上面就好。
我是覺得多一點還沒關係。
另外,這只適合utf8。我當時懶的寫編碼判斷了。反正是要自用的。
這是將中文字視為2格寬度。英文字視為1格寬度。
不過那時因為排板的問題。英文字明顯寬度會小很多。
所以可以調整eLen或eLen值。來做微調處理。
function hstar_utf8substr($str, $start, $len)
{
$l = 0; //字串指標位置
$addStr = 0; //字串開始加入
$getLen = 0; //已取得的長度
$cLen=2; //中文計算長度(寬度)
$eLen=1; //英數計算長度(寬度)
$unLen = strlen($str);
$getStr = '';
for($lc=0;$lc<$unLen;$lc++){
$tempStr = substr($str,$lc,3);
if(mb_strlen($tempStr)==1){
//這個是中文,計算寬度2
$lc += 2;
$getLen +=$cLen;
}else{
//這不是中文,重取一格
$tempStr = substr($str,$lc,1);
$getLen +=$eLen;
}
//是否超過起始位置
if($getLen>=$start){
$getStr.=$tempStr;
}
//取得長度足夠時,跳出
if($getLen-$start >= $len){
break;
}
}
return $getStr;
}
不過那時因為排板的問題。英文字明顯寬度會小很多。
這主要跟字型有關,只有等寬字型,英文字才會剛好是中文的一半。
(一般程式編輯器顯示畫面,用的就是等寬字型)
如果不是等寬字型,英文往往會有一些調整,會比較好看。
嗯,我那時其實是將英文字調+0.8。這樣會整體寬度+-值不會超過40px
要不然差異40px。一整列下來很難看。
另外再說一件事。其實我曾經有過一次,是連英數也要分離的。
因為英文跟數字的寬度有些許誤差。英文字寬度較小,但數字則沒變。
但客戶就是喜歡用這個字型。
只好再做英數的判斷了。
不過那個我就沒保留了。那是我後期改的。
十分感恩啊,
程式碼非常精巧,我很喜歡。
若是用細明體的話,中文應該就會是英數的2倍了,視覺比例上可能比較剛好。
跟字體比較沒關係,跟字型檔比較有關係。
淺水員有說了,如果選等寬字型檔才會是剛好二倍。
怕的不是等寬的才頭痛。
要精細計算的話,我可能會用 fontkit 去分析字型裡面每個字的寬度
預先做成對照表(其實只有英數而已,其他全形字大小都是一樣的)
下面這程式碼可以看出某個字型檔每個字的寬度
(主要是半形字,全形字我隨便找幾個測試而已)
const fontkit = require('fontkit');
const path = require('path');
var fontFile=path.resolve(__dirname, './SourceHanSansTW-Light.otf');
var font = fontkit.openSync(fontFile);
console.log(`unitsPerEm: ${font.unitsPerEm}`);
for(let code=0; code<128; ++code) {
if(!font.hasGlyphForCodePoint(code)) {
continue;
}
glyph = font.glyphForCodePoint(code);
console.log(`${code} ${String.fromCodePoint(code)} ${glyph.advanceWidth}`);
}
let s='丨川, '; //全形字測試
Array.from(s).forEach((c)=>{
let code=c.codePointAt();
if(!font.hasGlyphForCodePoint(code)) {
return;
}
glyph = font.glyphForCodePoint(code);
console.log(`${code} ${String.fromCodePoint(code)} ${glyph.advanceWidth}`);
});
哈哈,果然也是用同一招。
雖然不太相同。我之前是用gd函數計算過。但無法很精準。
某些字型就是很他xx的怪。且也很操效能。
不過想說反正只計算一次也沒差。
但碰到很頭大的字型,gd不吃或ttf不吃。就有點頭大。
後來是自已用了js跑計算寬度儲存下來用的。用js計算出來的會比較精準。都是滿滿的。