Skip to content

Commit 8ab2932

Browse files
author
WebCoder49
committed
Start fixing character width in Firefox as still problems there; chromium works well
1 parent 91b8c0c commit 8ab2932

File tree

2 files changed

+96
-17
lines changed

2 files changed

+96
-17
lines changed

plugins/special-chars.css

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,53 @@
44
*/
55

66
.code-input_special-char {
7-
display: inline;
7+
display: inline-block;
88
position: relative;
99
top: 0;
1010
left: 0;
11+
12+
height: 1em;
13+
/* width: set by JS */
14+
15+
text-decoration: none;
16+
text-shadow: none;
1117
}
1218

1319
.code-input_special-char::before {
1420
content: attr(data-top);
1521
color: inherit;
22+
background-color: inherit;
1623

17-
font-size: calc(50% - 2px);
18-
24+
font-size: calc(100% - 2px);
1925
position: absolute;
20-
top: 0;
26+
line-height: normal;
27+
top: -1em;
2128
left: 0;
2229
padding: 0;
2330
padding-bottom: 2px;
24-
2531
border-top: 1px solid;
2632
border-left: 1px solid;
2733
border-right: 1px solid;
34+
35+
height: 1.5em;
36+
width: calc(100% - 2px);
2837
}
2938
.code-input_special-char::after {
3039
content: attr(data-bottom);
3140
color: inherit;
41+
background-color: inherit;
3242

33-
font-size: calc(50% - 2px);
34-
43+
font-size: calc(100% - 2px);
3544
position: absolute;
36-
bottom: 0;
45+
line-height: normal;
46+
bottom: -1em;
3747
left: 0;
3848
padding: 0;
3949
padding-top: 2px;
40-
4150
border-bottom: 1px solid;
4251
border-left: 1px solid;
4352
border-right: 1px solid;
53+
54+
height: 1.5em;
55+
width: calc(100% - 2px);
4456
}

plugins/special-chars.js

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,97 @@
55
codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
66
specialCharRegExp;
77

8+
cachedColors; // ascii number > [background color, text color]
9+
cachedWidths; // character > character width
10+
canvasContext;
11+
812
/**
913
* Create a special characters plugin instance
1014
* @param {RegExp} specialCharRegExp The regular expression which matches special characters
1115
*/
1216
constructor(specialCharRegExp = /(?!\n)(?!\t)[\u{0000}-\u{001F}]|[\u{0080}-\u{FFFF}]/ug) { // By default, covers many non-renderable ASCII characters
1317
super();
1418
this.specialCharRegExp = specialCharRegExp;
19+
this.cachedColors = {};
20+
21+
this.cachedWidths = {};
22+
23+
let canvas = document.createElement("canvas");
24+
window.addEventListener("load", () => {
25+
document.body.appendChild(canvas);
26+
27+
});
28+
this.canvasContext = canvas.getContext("2d");
29+
this.canvasContext.fillStyle = "black";
30+
this.canvasContext.font = "20px 'Consolas'"; // TODO: Make dynamic
1531
}
1632

1733
/* Runs after code is highlighted; Params: codeInput element) */
1834
afterHighlight(codeInput) {
1935
let result_element = codeInput.querySelector("pre code");
20-
21-
console.log("Text", result_element.innerHTML);
22-
23-
result_element.innerHTML = result_element.innerHTML.replaceAll(this.specialCharRegExp, this.specialCharReplacer);
36+
result_element.innerHTML = result_element.innerHTML.replaceAll(this.specialCharRegExp, this.specialCharReplacer.bind(this));
2437
}
2538

2639
specialCharReplacer(match_char, _match_char, index, whole_string, groups) {
27-
let hex_code = match_char.codePointAt(0).toString(16);
28-
hex_code = ("0000" + hex_code).substring(hex_code.length);
40+
let hex_code = match_char.codePointAt(0);
41+
42+
let colors = this.getCharacterColor(hex_code);
43+
44+
hex_code = hex_code.toString(16);
45+
hex_code = ("00" + hex_code).substring(hex_code.length); // So 2 chars with leading 0
2946
hex_code = hex_code.toUpperCase();
30-
console.log(match_char, hex_code);
31-
return `<span class='code-input_special-char' data-top='${hex_code.substr(0, 2)}' data-bottom='${hex_code.substr(2, 2)}'> </span>`;
47+
48+
let char_width = this.getCharacterWidth(match_char);
49+
console.log(hex_code, char_width);
50+
51+
// let background_color = hashed_last_hex_char + hashed_last_hex_char + hex_code.substr(0, 1) + hex_code.substr(0, 1) + hex_code.substr(1, 1) + hex_code.substr(1, 1) // So good range of colours
52+
return `<span class='code-input_special-char' data-top='${hex_code[0]}' data-bottom='${hex_code[1]}' style='background-color: #${colors[0]}; color: ${colors[1]}; width: ${char_width}px'></span>`;
3253
}
3354

55+
getCharacterColor(ascii_code) {
56+
// Choose colors based on character code - lazy load and return [background color, text color]
57+
let background_color;
58+
let text_color;
59+
if(!(ascii_code in this.cachedColors)) {
60+
// Get background color - arbitrary bit manipulation to get a good range of colours
61+
background_color = ascii_code^(ascii_code << 3)^(ascii_code << 7)^(ascii_code << 14)^(ascii_code << 16); // Arbitrary
62+
background_color = background_color^0x1fc627; // Arbitrary
63+
background_color = background_color.toString(16);
64+
background_color = ("000000" + background_color).substring(background_color.length); // So 6 chars with leading 0
65+
66+
// Get most suitable text color - white or black depending on background brightness
67+
let color_brightness = 0;
68+
for(let i = 0; i < 6; i += 2) {
69+
color_brightness += parseInt(background_color.substring(i, i+2), 16);
70+
}
71+
// Calculate darkness
72+
text_color = color_brightness < (128*3) ? "white" : "black";
73+
74+
this.cachedColors[ascii_code] = [background_color, text_color];
75+
return [background_color, text_color];
76+
} else {
77+
return this.cachedColors[ascii_code];
78+
}
79+
}
80+
81+
getCharacterWidth(char) {
82+
// Lazy-load - TODO: Get a cleaner way of doing this
83+
if(char in this.cachedWidths) {
84+
return this.cachedWidths[char];
85+
}
86+
87+
this.canvasContext.fillText(char, 10, 20);
88+
let width = this.canvasContext.measureText(char).width;
89+
if(width > 20) {
90+
width /= 2; // Fix double-width-in-canvas Firefox bug
91+
} else if(width == 0) {
92+
let fallbackWidth = this.getCharacterWidth("\u0096");
93+
this.cachedWidths[char] = fallbackWidth;
94+
return fallbackWidth; // In Firefox some control chars don't render
95+
}
96+
console.log(char, this.canvasContext.measureText(char));
97+
98+
this.cachedWidths[char] = width;
99+
return width;
100+
}
34101
}

0 commit comments

Comments
 (0)