背景:合同模板场景,css中linear-gradient渐变实现的信纸格式(每行内容不管是否占满,底线都到行末),因在wkhtmltopdf工具把网页转pdf时有各种兼容和效果问题,最后只能用js强行拆分行,每行加下划线效果。
问题描述
起初,汉字按1em宽度计算,其他非汉字类按半个汉字(0.5em)计算,应用了不少合同模板html,没出现问题。直到海南安居房合同,要求打印替换后的内容也要加粗后。即:信纸格式里的内容,不加粗时,显示正常,加粗时,显示就超过原本计算的一行,自动换行到下一行了。见下图:

加粗前.png

加粗.png
也就是说:宋体,加粗前后,字符的实际宽度变了。
字体实际宽度调研
仅对宋体,因为制式合同都是宋体为字体的。针对中文、英文、数字、特殊-英(特殊字符-半角)、特殊-中(特殊字符-全角)这些字符,做了加粗前后的对比。
-
win10系统(windows系统)下效果:
字符宽度-win10.png -
win11系统(windows系统)下效果:
字符宽度-win11.png -
macOS系统(苹果系统)下效果:
字符宽度-苹果系统.png
对比了一下,发现:
- 在windows系统下,不加粗时,符合非汉字字符是半个汉字字符的宽度。在苹果系统下,就比较杂乱了。非汉字字符,有超过半个汉字宽度的,有小于半个汉字宽度。
- 加粗后,不管是windows系统下,还是苹果系统下,,都比较乱。
结论
综上所述,加粗前后都无法精准的算每行字符的实际长度。仍旧只能采用临时调整宽度,比如:每行26个汉字,js计算按26汉字计算,每行样式临时改成26.4em的宽度,以保证正常显示。
附上调研源码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<title>宋体加粗前后的字符实际宽度</title>
<style>
table {
margin: 0 30px;
font-size: 14px;
font-family: '宋体';
border: 1px solid #aaa;
border-collapse: collapse;
}
th,
td {
border: 1px solid #aaa;
padding: 5px;
}
</style>
</head>
<body>
<div>
<table>
<thead>
<tr>
<th>类型</th>
<th>字符</th>
<th>实际大小<br>(20px为参考)</th>
<th>em<br>实际大小/20)</th>
<th>加粗字符</th>
<th>加粗后实际大小<br>(20px为参考)</th>
<th>em<br>(实际大小/20)</th>
<th>加粗前后对比<br>(加粗后width/加粗前width)</th>
</tr>
</thead>
<tbody>
<!-- <tr>
<td>中文</td>
<td>汉</td>
<td>20px</td>
<td>1</td>
<td style="font-weight: bold;">汉</td>
<td>20px</td>
<td>1</td>
<td>1</td>
</tr> -->
</tbody>
</table>
</div>
<script>
function measureTextWidth(text, font) {
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
context.font = font;
return context.measureText(text).width;
}
// 使用示例
// const width = measureTextWidth('中', 'bold 16px SimSun'); // SimSun 是宋体
// console.log(`加粗后的字符宽度为 ${width}px`);
var charObj = {
'中文': '黎妃',
'英文': 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'数字': '0123456789',
'特殊-英': '\,.;$^*""*()-+=|<>&%#@!~',
'特殊-中': ',。、;?!()【】:“”‘’《》¥·',
};
var tbody = document.querySelector('tbody');
var strHtml = '';
Object.keys(charObj).forEach(function (key) {
var str = charObj[key];
str.split('').forEach(function (char) {
var width = measureTextWidth(char, '20px 宋体');
var widthBold = measureTextWidth(char, 'bold 20px 宋体');
var percent = width / 20;
var percent2 = widthBold / 20;
var percent3 = widthBold / width;
strHtml +=`<tr>
<td>${key}</td>
<td>${char}</td>
<td>${width}</td>
<td>${percent}</td>
<td style="font-weight: bold;">${char}</td>
<td>${widthBold}</td>
<td>${percent2}</td>
<td>${percent3}</td>
</tr>`;
})
});
tbody.innerHTML = strHtml;
</script>
</body>
</html>


