[READ-ONLY] Mirror of https://github.com/tynanpurdy/real-font-size. Tiny calculator that tells you the real dimensions of text, not its bounding box
tynanpurdy.github.io/real-font-size/
5.8 kB
182 lines
1const fontInput = document.getElementById("fontInput");
2const fontWeightInput = document.getElementById("fontWeightInput");
3const typeSizeInput = document.getElementById("typeSizeInput");
4const typeUnitsInput = document.getElementById("typeUnitsInput");
5const previewText = document.getElementById("previewText");
6const diagram = document.getElementById("diagram");
7const testStringInput = document.getElementById("testStringInput");
8
9class Font {
10 constructor(name = "Arial", weight = 400, size = 12, units = "pt") {
11 (this.name = name),
12 (this.weight = weight),
13 (this.size = size),
14 (this.units = units);
15 }
16
17 get style() {
18 return `${this.weight} ${this.size}${this.units} ${this.name}`;
19 }
20}
21
22previewText.style.font = new Font().style;
23
24// FUNCTIONS
25function unitsCorrectedSize(size, units) {
26 if (units === "pt") {
27 return size * (72.0 / 96.0);
28 }
29 return size;
30}
31
32function getRealFontHeightPx(textMetrics) {
33 return (
34 textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent
35 );
36}
37
38function getFontBoundingHeightPx(textMetrics) {
39 return textMetrics.fontBoundingBoxAscent + textMetrics.fontBoundingBoxDescent;
40}
41
42function clearCanvas(canvas) {
43 const ctx = canvas.getContext("2d");
44 ctx.clearRect(0, 0, canvas.width, canvas.height);
45 return ctx;
46}
47
48function drawDiagram(font = new Font(), testString = "Apd", canvas = diagram) {
49 const ctx = clearCanvas(canvas);
50
51 // measure the chosen font
52 ctx.font = font.style;
53 const sampleText = testString;
54 const textMetrics = ctx.measureText(sampleText);
55
56 // display the real height
57 const realHeightPx = getRealFontHeightPx(textMetrics);
58 document.getElementById("realHeight").textContent = `${unitsCorrectedSize(
59 realHeightPx,
60 font.units
61 )} ${font.units}`;
62 console.log(textMetrics);
63
64 // create a fixed-size matching diagram font
65 const diagramFont = new Font(font.name, font.weight, 18, "rem");
66 ctx.font = diagramFont.style;
67 const diagramTextMetrics = ctx.measureText(sampleText);
68
69 // resize canvas to fit diagram text
70 const sampleTextX = 0;
71 const sampleTextY = diagramTextMetrics.fontBoundingBoxAscent;
72 canvas.width = diagramTextMetrics.width;
73 canvas.height = getFontBoundingHeightPx(diagramTextMetrics);
74
75 // draw bounding-box background
76 ctx.beginPath();
77 ctx.fillStyle = "#282828";
78 ctx.fillRect(
79 sampleTextX,
80 0,
81 diagramTextMetrics.width,
82 diagramTextMetrics.fontBoundingBoxAscent +
83 diagramTextMetrics.fontBoundingBoxDescent
84 );
85 ctx.stroke();
86
87 // draw diagram text
88 ctx.font = diagramFont.style;
89 ctx.fillStyle = "white";
90 ctx.fillText(sampleText, sampleTextX, sampleTextY);
91
92 const baselinesAboveAlphabetic = ["actualBoundingBoxAscent"];
93 const baselinesBelowAlphabetic = ["actualBoundingBoxDescent"];
94 const baselines = [...baselinesAboveAlphabetic, ...baselinesBelowAlphabetic];
95 ctx.strokeStyle = "red";
96
97 baselines.forEach((baseline) => {
98 const y = sampleTextY;
99 ctx.beginPath();
100
101 const baselineMetricValue = diagramTextMetrics[baseline];
102 if (baselineMetricValue === undefined) {
103 return;
104 }
105
106 const lineY = baselinesBelowAlphabetic.includes(baseline)
107 ? y + Math.abs(baselineMetricValue)
108 : y - Math.abs(baselineMetricValue);
109 ctx.moveTo(sampleTextX, lineY);
110 ctx.lineTo(sampleTextX + diagramTextMetrics.width, lineY);
111 ctx.stroke();
112 });
113}
114drawDiagram();
115
116// EVENT HANDLERS
117typeInputs.addEventListener("change", () => {
118 newFont = new Font();
119 newFont.name = fontInput.value;
120 newFont.weight = fontWeightInput.value;
121 newFont.size = typeSizeInput.value;
122 newFont.units = typeUnitsInput.value;
123
124 previewText.style.font = newFont.style;
125 drawDiagram(newFont);
126});
127
128document
129 .getElementById("copyButton")
130 .addEventListener("click", async function () {
131 try {
132 const realHeight = document.getElementById("realHeight").textContent;
133
134 // Use the Clipboard API to copy the text
135 await navigator.clipboard.writeText(realHeight);
136
137 // signify value has been copied via button content
138 let originalText = this.textContent;
139 this.textContent = "✅";
140 setTimeout(
141 function () {
142 this.textContent = originalText;
143 }.bind(this),
144 1500
145 );
146 } catch (err) {
147 console.error("Failed to copy: ", err);
148 }
149 });
150
151testStringInput.addEventListener("change", () => {
152 const testString = this.value.trim() !== "" ? this.value : "Apd";
153 drawDiagram();
154 console.log(`new test string: ${testString}`);
155});
156
157document.getElementById("launchFonts").addEventListener("click", () => {
158 if (navigator.userAgent.toUpperCase().indexOf("MAC") >= 0) {
159 window.open("fontbook://");
160 } else if (navigator.userAgent.toUpperCase().indexOf("WIN") >= 0) {
161 window.open("ms-settings:fonts");
162 } else if (navigator.userAgent.includes("Linux")) {
163 alert(
164 "I'm a novice programmer, ok? You chose linux and you knew the consequences when you did so. I have no clue how to open your font manager, if you even bothered to install one. Your fonts are in a folder somewhere, or three, I have no idea. Have fun searching, I believe in you."
165 );
166 } else if (navigator.userAgent.includes("Android")) {
167 alert(
168 "Font management is not available on Android. I don't make the rules. You can yell at Sundar Pichai if you want."
169 );
170 } else if (
171 navigator.userAgent.includes("iPhone") ||
172 navigator.userAgent.includes("iPad")
173 ) {
174 alert(
175 "Font management is not available on iOS. I don't make the rules. You can yell at Tim Apple if you want."
176 );
177 } else {
178 alert(
179 "Whatever OS you're running is not easily identified by my simple if statements. Good on you. As for your fonts, I have no clue where they are."
180 );
181 }
182});