2 // Copyright (c) 2018 Dimitar Toshkov Zhekov <dimitar.zhekov@gmail.com>
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License as
6 // published by the Free Software Foundation; either version 2 of
7 // the License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
17 const fnutil = require('./fnutil.js');
18 const bdf = require('./bdf.js');
22 constructor(code, name, width, data) {
29 static from(char, fbbox) {
30 const deltaYOff = char.bbx.yoff - fbbox.yoff; // ~DSB
34 if (char.dwidth.x >= 0) {
35 if (char.bbx.xoff >= 0) {
36 width = Math.max(char.bbx.width + char.bbx.xoff, char.dwidth.x);
37 dstXOff = char.bbx.xoff;
39 width = Math.max(char.bbx.width, char.dwidth.x - char.bbx.xoff);
43 dstXOff = Math.max(char.bbx.xoff - char.dwidth.x, 0);
44 width = char.bbx.width + dstXOff;
47 if (width > bdf.WIDTH_MAX) {
48 throw new Error(`char ${char.code}: output width > ${bdf.WIDTH_MAX}`);
50 if (char.bbx.yoff < fbbox.yoff) {
51 throw new Error(`char ${char.code}: BBX yoff < FONTBOUNDINGBOX yoff`);
54 const height = fbbox.height;
55 const srcRowSize = char.bbx.rowSize();
56 const dstRowSize = (width + 7) >> 3;
57 const dstYMax = height - deltaYOff;
58 const dstYMin = dstYMax - char.bbx.height;
59 const compatRow = dstXOff === 0 && width >= char.bbx.width;
62 if (compatRow && srcRowSize === dstRowSize && dstYMin === 0 && dstYMax === height) {
64 } else if (dstYMin < 0) {
65 throw new Error(`char ${char.code}: start row ${dstYMin}`);
67 data = Buffer.alloc(dstRowSize * height);
69 for (let dstY = dstYMin; dstY < dstYMax; dstY++) {
70 let srcByteNo = (dstY - dstYMin) * srcRowSize;
71 let dstByteNo = dstY * dstRowSize + (dstXOff >> 3);
74 char.data.copy(data, dstByteNo, srcByteNo, srcByteNo + srcRowSize);
77 let dstBitNo = 7 - (dstXOff & 7);
79 for (let x = 0; x < char.bbx.width; x++) {
80 if (char.data[srcByteNo] & (1 << srcBitNo)) {
81 data[dstByteNo] |= (1 << dstBitNo);
96 return new Char(char.code, char.props.get('STARTCHAR'), width, data);
100 return (this.width * (this.data.length / this.rowSize()) + 7) >> 3;
104 return (this.width + 7) >> 3;
107 write(output, height, yoffset) {
108 let header = `STARTCHAR ${this.name}\nENCODING ${this.code}\n`;
109 const swidth = fnutil.round(this.width * 1000 / height);
111 header += `SWIDTH ${swidth} 0\nDWIDTH ${this.width} 0\nBBX ${this.width} ${height} 0 ${yoffset}\n`;
112 output.writeLine(header + 'BITMAP\n' + bdf.Char.bitmap(this.data, this.rowSize()) + 'ENDCHAR');
117 class Font extends bdf.Font {
120 this.minWidth = bdf.WIDTH_MAX;
128 this.chars = this.chars.map(char => Char.from(char, this.bbx));
130 this.chars.forEach(char => {
131 this.minWidth = Math.min(this.minWidth, char.width);
132 this.bbx.width = Math.max(this.bbx.width, char.width);
133 totalWidth += char.width;
135 this.avgWidth = fnutil.round(totalWidth / this.chars.length);
136 this.props.set('FONTBOUNDINGBOX', this.bbx.toString());
141 return (new Font())._read(input);
145 return Number(this.bbx.width > this.minWidth);
149 this.props.forEach((name, value) => output.writeProp(name, value));
150 this.chars.forEach(char => char.write(output, this.bbx.height, this.bbx.yoff));
151 output.writeLine('ENDFONT');
156 module.exports = Object.freeze({