node根据图片高度计算文字字号

之前做过这么一个需求,有一系列的字体文件,需要解析字体文件并根据解析出的字体文件的名字动态生成高度一致的字体预览图,这里有2个难点:

1、解析字体文件

对于解析字体文件的信息,我们可以通过库opentype.js来实现,关于opentype.js库的安装和使用可以看:https://www.npmjs.com/package/opentype.js,使用没有什么难度,这里有一点要提的是有些字体文件被二次修改过,出来的字体信息不规范,或者完全不符合我们的预期,对于这部分字体文件我们可能需要做一些特殊的处理或者直接抛弃使用。

2、生成高度一致的字体预览图

对于不同的字体,如果我们用相同的字号去绘制字体预览图,那么得到的图片的高度肯定是不一致的,比较每个字体中字符图设计的大小是不一样的,如果需要生成高度一致的字体预览图,相当于需要我们根据图片的高度去计算出字号的大小,嗯,这个问题不会,老师告诉我们,遇到不会的问题,要学会搜索答案,于是乎开始了搜索找寻答案之路

一通搜索后,终于还是没找到答案,难道就该死心放弃吗?作为程序员的我们应该有锲而不舍的精神,为了最终的正确的答案不停奋斗。

经过一番思考,想起了一个问题,我们的浏览器可以根据我们在css中设定字体和字号值渲染出正确的文字,而且在css设定好字体和字号后我们可以通过js动态的获取到此时文本节点的高度,那反过来是不是我们可以通过文本节点的告诉来推断当前的字体呢?这里我们知道了2个点:

1、要用浏览器渲染字体。

2、根据浏览器渲染出来的文字节点的高度来推算字号

第一个点,在node中如果要使用浏览器,地球人都知道应该选用puppeteer,文档地址:http://www.puppeteerjs.com/

第二个点, 根据浏览器渲染出来的文字节点的高度来推算字号,直接反推肯定是行不通的,但是换个思路,一个字号应该对应着不同的字体高度,假设字号12对应高度20,那么字号13可能对应的就是高度22,以此类推,我们通过遍历一个字号范围比如12-22,css动态的设置字号,获取当前字号的文字节点的高度,如果近似的等于我们的目标高度,然后就返回字号,如果不满足就继续往后循环,直到找到正确的答案,这样子我们是不是就通过字体预览图高度推算出了字号呢?知道了原理,实现就相对简单了,这里贴出一个简单的实现:

async function getFontSize(fontUrl, fullname, fontHeight, text) {
	    let document = `
		<html
		<head>
	        <meta charset="utf-8" />
			<style>
				@font-face {
					font-family: "CF";
					font-style: normal;
					font-weight: 400;
					src: url("${fontUrl}");
		        }
		        html, body {
		            font-family: 'CF', sans-serif;
		        }
	        </style>
	      </head>
	      <body>
	        <span id="tt"></span>
	      </body>
	    </html>
	    `;

	    await page.goto("data:text/html;charset=UTF-8," + document, { waitUntil: 'networkidle0' }).catch(e => console.error(e));
	    await page.evaluateHandle('document.fonts.ready');
	    return await page.evaluate((fontHeight, text) => {
	    	const span = document.getElementById('tt');
	    	span.innerText = text;
	    	function getFontSize(height) {
	    		let min = +Infinity, ret = {};
	    		for (let i=12; i<=40; i++) {
	    			span.style.fontSize = `${i}px`;
	    			const diff = Math.abs(span.offsetHeight - height);
	    			if (diff  === 0) {
	    				return {
	    					width: span.offsetWidth,
	    					height,
	    					fontSize: i
	    				}
	    			}
	    			if (diff < min) {
	    				min = diff;
	    				ret = {
	    					width: span.offsetWidth,
	    					height,
	    					fontSize: i
	    				};
	    			}
	    		}
	    		return ret;
	    	}

	    	return getFontSize(fontHeight);
	    }, fontHeight, text);
}

这里需要注意2个问题:

1、从12开始是因为正常chrome能渲染的最小字号是12,当然可以通过别的方法显示小于字号12的字体,这个大家自行研究。

2、await page.evaluateHandle('document.fonts.ready');是等到字体文件加载完成,这个是puppeteer官方提供的。

  • 支付宝二维码 支付宝
  • 微信二维码 微信
相关文章