Topaz font does not properly display backslashes and certain characters when rendered on Canvas

Who would have thought I'd stumble upon a new challenge on Stack Overflow? But here we are. If there's already a solution out there, please guide me in the right direction. I'm currently developing an interactive desktop environment inspired by the Amiga Workbench, and naturally, I want to include a recreation of the iconic Topaz font. However, I've encountered an issue. While basic text like hyphens, commas, and periods display correctly when drawn using the Canvas API, symbols like the copyright sign or backslash do not.

Amiga Workbench screen where the copyright sign isn't displayed correctly

I initially thought that the font I'm using may be missing these symbols, but it seemed unlikely. Why create a font only to omit certain characters, right? Upon inspecting the font with Font Forge, I can see that the symbols are indeed included along with a variety of other characters.

Font Forge displaying a set of symbols saved in the font data

Examining the complete set rules out any Unicode mix-ups. Moreover, when I used the same font in Gimp for a visual mockup, the copyright symbol displayed without any issues. So keyboard layouts shouldn't be causing this problem, right?

Screenshot from Gimp showing correct display of the copyright sign

The only remaining factor is my code, which I believe is free of issues. Here's how I integrate the font and draw text:

const amigaFont = new FontFace(
    'Topaz A1200',
    'url(./media/font/Topaz_a1200_v1.0.ttf)'
);
amigaFont.load().then(function (font) {
    document.fonts.add(amigaFont);
});
// Additional code here ensures all media, including images and fonts, are loaded before drawing on the canvas
const splashScreenText = [
    'AMIGA ROM Operating System and Libraries',
    'Copyright © 1985-1992 Commodore-Amiga, Inc.',
    'All Rights Reserved.',
    '1>'
];
let fontSize = Math.round(amigaDOSwindow.bottom * (24 / 356));
ctx.font = fontSize.toString() + 'px Topaz A1200';
textCursor.x = amigaDOSinput.left;
textCursor.y = amigaDOSinput.top + fontSize;
for (let index = 0; index < splashScreenText.length; index++) {
    ctx.fillText(splashScreenText[index], textCursor.x, textCursor.y);
    textCursor.y += fontSize;
}

Edit: Requested to provide runnable code replicating the issue:

const mediaCount = 1;
const mediaArray = [];
const mediaManager = setInterval(checkArray, 15);
function checkArray() {
    switch (true) {
        case mediaArray.length == mediaCount:
            clearInterval(mediaManager);
            initCanvas();
            break;
        default:
            break;
    }
}
const amigaFont = new FontFace(
    'Topaz A1200',
    'url(https://cdn.jsdelivr.net/gh/rewtnull/amigafonts@master/ttf/Topaz_a1200_v1.0.ttf)'
);
amigaFont.load().then(function (font) {
    document.fonts.add(amigaFont);
    mediaArray.push(amigaFont);
});
const splashScreenText = [
    'AMIGA ROM Operating System and Libraries',
    'Copyright © 1985-1992 Commodore-Amiga, Inc.',
    'All Rights Reserved.',
    '1>'
];
const textCursor = {
    x: 0,
    y: 0,
};
function initCanvas() {
    let canvas = document.createElement('canvas');
    canvas.id = 'canvas';
    document.body.appendChild(canvas);
    processCanvas();
}
function processCanvas() {
    let canvas = document.getElementById('canvas');
    canvas.width = 320;
    canvas.height = 180;
    drawText();
}
function drawText() {
    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext('2d');
    let fontSize = 32;
    ctx.font = fontSize.toString() + 'px Topaz A1200';
    textCursor.x = 0;
    textCursor.y = 0 + fontSize;
    for (let i = 0; i < splashScreenText.length; i++) {
        ctx.fillText(splashScreenText[i], textCursor.x, textCursor.y);
        textCursor.y += fontSize;
    }
}
<canvas id="canvas"></canvas>

Surprisingly, everything seems to work fine in this setup...

Could there be something unknown about fonts and the Canvas API contributing to this anomaly?

Answer №1

When you see a replacement character (U+FFFD), it indicates that the issue is not related to the font, as a different font would be used in that case instead of the replacement glyph. This suggests that the problem lies with encoding.

It's important to ensure that your document and HTML file are using the same charset. If you save your page as UTF-8 and include

<meta charset="utf-8">
in the head section of your document, the glyphs should display correctly, just like they do in your code snippet.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issue encountered in IE9 and IE10 when loading the angular library results in the "SCRIPT5007: Object expected" error for Angular JS

I am currently working on an AngularJS application that needs to be compatible with Firefox, IE 9, and IE 10. We are using the latest version of the AngularJS library (currently at 1.3.15). Our serverside is based on Java in the JavaEE platform, running on ...

"Troubleshooting IE-specific problem: Loading dropdown options dynamically with AJAX on change

Struggling with a particular issue lately. I'm attempting to populate a second dropdown menu based on the selection made in the first one using jquery ajax. Surprisingly, it works flawlessly on all browsers except for IE 11. Below is the ajax functio ...

Submitting a form and using Ajax to upload an image

Is there a way to submit an image file to php using ajax without assigning the file to a variable with onchange event? I've tried triggering the request on submit click, but keep getting the error message: "cannot read property 0 of undefined." <ht ...

What is the best way to eliminate an item from an array in JavaScript or AngularJS?

I'm attempting to eliminate objects from an array and retrieve the resulting array. I've been using a remove function, but it's not functioning as expected. Here is the input I'm working with: The goal is to remove all values in the ar ...

Pressing the button on the form has no effect

I am testing a login screen using NodeJS, but I am facing an issue where the submit button does not redirect to the dashboard screen as intended. What could be missing in the code? The submit button should direct to the dashboard screen that I have created ...

A ServiceWorker used a promise in FetchEvent.respondWith() that resulted in a non-Response value of 'undefined'. This caused an issue in browser-sync

There are times when my server encounters an error in the console: Failed to load ‘http://localhost:3000/browser-sync/socket.io/?EIO=3&transport=polling&t=Lm2wn4p’. A ServiceWorker passed a promise to FetchEvent.respondWith() that reso ...

Using JavaScript to transfer the data ID from a button to a form

I am working on a project where I have a button that triggers a modal: <button type="button" class="btn btn-primary add-subscription" data-toggle="modal" data-workspace_id="{{ workspace.id }}" data-target="# ...

What is the best way to modify Mega Menus using JavaScript?

I am currently managing a website that features "mega menu" style menus. These menus consist of nested <UL> elements and contain approximately 150 to 200 entries, resulting in a heavy load on the page and posing challenges for screen readers. To add ...

Differentiate various collections of shapes

My application has a specific workflow: 1) Next to the name of Patient X, there is a "Check In" button. Once clicked, this action is recorded on the server side. 2) Upon clicking the "Check In" button, a dropdown menu appears with various locations for t ...

Trigger a child-mounted event and retrieve it from the parent component

Imagine I have a component named child. There is some data stored there that I need to retrieve in the parent component. To accomplish this, I plan to emit an event in the childs mount using this.$emit('get-data', this.data), and then receive it ...

struggling with handling form data in Express/Node application

Currently this is my setup: Below is the Post method I am using app.post('/barchartentry', function(req, res){ res.render('barchart', { title: 'EZgraph: Bar Chart', barValues: req.body.myInputs, labelValues: req.body ...

Identifying the position of an element as the first, nth, or last child within its parent using ReactJS

I'm currently experimenting with dynamic functionality in ReactJS and I need to determine the position of a child relative to its parent. How can I achieve this in ReactJS? I have come across this post which discusses detecting relationships between ...

Converting an Array of Users to an Array of Email Addresses Using Google Apps Script

I am currently in the process of creating a script using Google Apps Script. My goal is to verify whether a user with the email address [email protected] has viewing or editing privileges for a folder. If the user does not have either privilege, I wa ...

Transforming a JSON into a JavaScript object using deserialization

Within a Java server application, there exists a string that can be accessed through AJAX. The structure of this string is exemplified below: var json = [{ "adjacencies": [ { "nodeTo": "graphnode2", "nodeFrom": "graphnode1" ...

Utilizing AJAX in Wordpress to Dynamically Update HREF Links

My website now has AJAX functionality, and you can see a live example at www.mathewhood.com. I am interested in changing the URL format when clicked from To something like , without the /sitefiles/ for security reasons. Below is my code. If anyone is ex ...

Error: "Access-Control-Allow-Origin" header is missing in Firebase Function

I have encountered an issue with my firebase functions GET request. While I am able to receive data using Postman, I am facing difficulties when trying to fetch data from my front-end application. Upon accessing the endpoints, I am seeing the following er ...

I am encountering an error when attempting to import the app from the server.js file in Express

In my server.js file, I have set up an express server and exported the app from it. //server.js require("dotenv").config(); const express = require("express"); const app = express(); const connectToDb = require("./connectToDb ...

Maintaining the original layout upon refreshing the page

Incorporating two forms on my website, I added a button that transitions between the login and sign-up forms smoothly. The process is as follows: Initially, the login form is displayed; clicking the button switches to the sign-up form. Proceeding to subm ...

Google Extension PHP Plugin

Is it feasible to integrate a PHP file into a Google Extension for Chrome? I've been exploring the idea of creating an extension and most resources only mention HTML, CSS, and JavaScript. Any guidance on using PHP in the extension would be highly valu ...

The functionality to disable and reenable a button after performing a calculation is not functioning properly in the code

Here's the issue I'm facing with my code: When I click the search button, a for loop prints "hi" 5000 times. Before this loop starts, I want to disable the button. However, after the console.log is done, the button should be enabled again. But fo ...