字符实际长度计算

背景:合同模板场景,css中linear-gradient渐变实现的信纸格式(每行内容不管是否占满,底线都到行末),因在wkhtmltopdf工具把网页转pdf时有各种兼容和效果问题,最后只能用js强行拆分行,每行加下划线效果。

问题描述

起初,汉字按1em宽度计算,其他非汉字类按半个汉字(0.5em)计算,应用了不少合同模板html,没出现问题。直到海南安居房合同,要求打印替换后的内容也要加粗后。即:信纸格式里的内容,不加粗时,显示正常,加粗时,显示就超过原本计算的一行,自动换行到下一行了。见下图:


加粗前.png
加粗.png

也就是说:宋体,加粗前后,字符的实际宽度变了。

字体实际宽度调研

仅对宋体,因为制式合同都是宋体为字体的。针对中文、英文、数字、特殊-英(特殊字符-半角)、特殊-中(特殊字符-全角)这些字符,做了加粗前后的对比。

  • win10系统(windows系统)下效果:


    字符宽度-win10.png
  • win11系统(windows系统)下效果:


    字符宽度-win11.png
  • macOS系统(苹果系统)下效果:


    字符宽度-苹果系统.png

对比了一下,发现:

  1. 在windows系统下,不加粗时,符合非汉字字符是半个汉字字符的宽度。在苹果系统下,就比较杂乱了。非汉字字符,有超过半个汉字宽度的,有小于半个汉字宽度。
  2. 加粗后,不管是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>
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容