0

I want my users to be able to select a font from their computer and update the webpage so that it uses that font.

<style>
html * { font-family: Arial !important; }
</style>

I use html * in css and I want to change the font family to user's file.

<script>
function change_css() {
    // ???
}
</script>
<div>
  <div</div>
  <input type="file" name="upload" id="upload"/>
  <input type="submit" value="submit" onclick="change_css();">
</div>

How to implement this?

Woodrow Barlow
  • 8,477
  • 3
  • 48
  • 86
younyokel
  • 327
  • 2
  • 15

3 Answers3

6

Changing the Webpage Font

In JavaScript, if you have a DOM element and want to change its font, you can do that by manipulating its CSSStyleDeclaration object. Specifically, by setting element.style.fontFamily.

For example,

document.body.style.fontFamily = 'Noto Sans, Arial, sans-serif';

This demo creates an HTML select element with a couple of font options. By default, the body's font is 'Arial', but the select element has an event handler attached so that when it is changed, the body's font is changed to match the newly selected option.

If you want to add more fonts to the list, simply add additional options to the select element, and set the value attribute to the font's name.

var fontSelector = document.getElementById('font-selector');

fontSelector.addEventListener('change', (event) => {
    document.body.style.fontFamily = event.target.value;
});
body { font-family: 'Arial'; }
<p>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

<label for="font-selector">Select Font</label>
<select id="font-selector">
  <option value="Arial">Arial</option>
  <option value="Times">Times</option>
  <option value="Courier">Courier</option>
</select>

Getting Fonts from the User

tl;dr: You can use any fonts that the user has installed, without them needing to upload them. But you can't know which fonts the user has installed, for privacy reasons.

When you specify the name of a font in CSS, the browser will use that font if it is present on the user's computer (or if it is provided as a webfont), otherwise the browser will revert to its default. There's no need to actually upload the font from the user's computer onto your server.

To rephrase: when the browser is doing font rendering, it gets the fonts from the user's computer, not from your server. There is no point in uploading a font from the user's computer to your server. When you use webfonts, you are sending a font from your server to the user's computer (it gets stored in a temporary location) and then the browser uses it. Note also that webfonts are typically not the same file format as fonts that the user has installed on their computer.

The caveat is, of course, that you don't know what fonts the user has on their computer (so you don't know which fonts to list in the select element for the example above). Most websites don't try to know, they simply use a "web-safe" font (i.e. font that is installed on most operating systems by default) or explicitly provide a webfont (often via services like Google Fonts).

JavaScript does not provide an API for knowing which fonts are installed on a user's computer. This is on purpose, because such information could be used to trivially 'fingerprint' visitors to a website. In general, allowing websites to know too much about the specifics of how a user's computer is set up would be considered a privacy concern.

There do exist libraries and scripts (this one, for example) that allow you to query whether a particular font is installed on the user's computer (not a list of all fonts installed, just a query "is font [x] installed?"). These work by making a few guesses and will not always be correct. There is also a way to get the full list of installed fonts using Adobe Flash (notice that domain is browserspy.dk, because this technique is used for fingerprinting as I discussed earlier), but most users do not have Flash installed these days.

The standard approach would be to offer any fonts you desire as webfonts, served to the user over the network.

A Hacky Solution

With all that said, here's something you can do, with a bit of elbow grease:

You can let a user choose a font file (no need to upload it to the server) and then use this javascript library to extract the font name from that file, then set the webpage's font to that name.

Now, if they just have a random font file sitting on their desktop or downloads folder or something, this won't work. The file the user provides must be a font that they actually have installed on their system.

var fontSelector = document.getElementById('font-selector');

fontSelector.addEventListener('change', (event) => {
  const fontFile = event.target.files[0];
  const fontReader = new FileReader();

  fontReader.onload = (e) => {
    try {
      const fontMeta = FontName.parse(e.target.result)[0];
      document.body.style.fontFamily = fontMeta.fontFamily;
    }
    catch(err) {
      // failed
    }
  };
  fontReader.readAsArrayBuffer(fontFile);
});
body { font-family: 'Arial'; }
<script src="https://danbovey.uk/fontname/index.js"></script>

<p>
  Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>

<label for="font-selector">Select Font</label>
<input type="file" id="font-selector" accept=".ttf,application/x-font-ttf">

A More Robust Solution

Web-based photo editing software accomplishes this sort of technique by taking over the font rendering entirely. Rather than letting the browser render text using the system fonts, they use javascript libraries that can parse font files and then create SVG definitions for each glyph. Then they manage the placement of each SVG on the webpage, including kerning, line-spacing, ligatures, etc. These javascript libraries are very large and complex, and will require you to do a lot more integration work to get it going.

To explore this option, you should start by taking a look at libraries such as Typr.js or opentype.js.

Woodrow Barlow
  • 8,477
  • 3
  • 48
  • 86
  • @Eadwine i've just updated this answer. what you want isn't 100% possible but i think i've gotten close. take a look and let me know if that's what you're looking for. – Woodrow Barlow Nov 01 '19 at 23:54
  • wow thanks for this! Somewhy it says me that the `font-selector` is null and shows me a fatal error. – younyokel Nov 02 '19 at 09:40
  • @EadwineYoun you're seeing that error here in my stackoverflow answer, or only after trying to integrate this solution into your code? i'm not seeing that error in my snippet here. can you provide the full text of the error? – Woodrow Barlow Nov 03 '19 at 03:18
  • I'm sorry, nevermind. I incorrectly copied the code from the source link. – younyokel Nov 03 '19 at 16:05
  • @EadwineYoun the javascript library i used claims to work with TTF and OTF. you'll need to update the `accept` attribute of the file picker to allow OTF. to support other font formats, you'll need to find another javascript library that can parse the font-family name out of the font file. – Woodrow Barlow Nov 04 '19 at 14:25
  • @EadwineYoun you might also want to look into [Typr.js](https://github.com/photopea/Typr.js) or [opentype.js](https://github.com/opentypejs/opentype.js), which can load ttf/otf fonts and then actually render text directly in that font on screen as SVGs. essentially the text rendering is done by the library rather than the browser. it would require much more work than what i've put together here, but would be much more robust. that's exactly the technique that online photo editors use. – Woodrow Barlow Nov 04 '19 at 14:29
0

You could just append a style tag containing the user's CSS to the document:

<html>
    <head>
        <script>
      var openFile = function(event) {
        var input = event.target;

        var reader = new FileReader();
        reader.onload = function(){
          var text = reader.result;
          var node = document.createElement('style');
          node.innerHTML = text;
          document.body.appendChild(node);
        };
        reader.readAsText(input.files[0]);
      };
        </script>
    </head>
    <body>
        <h1>TEST</h1>
        <input type='file' accept='text/css' onchange='openFile(event)'>
            <br>
                <div id='output'>

                </div>
            </body>
        </html>

If you upload a file containing this CSS, it should change the TEST text's color to red:

h1 {
 color: red;
}

ETA: To change font family:

html * {
    font-family: "Times New Roman", Times, serif;
}
gabriel.hayes
  • 2,267
  • 12
  • 15
  • Hey thank you for the example, but I don't think users should create their own CSS files just for changing the font. Is there a way of loading the TTF, OTF files and applying them as font family's? – younyokel Nov 01 '19 at 18:59
  • Yikes. That might be a bit more difficult since those files aren't plaintext and I believe CSS/Fonts need to be relatively located on a server to work. Maybe this [answer](https://stackoverflow.com/questions/11812111/font-face-url-pointing-to-local-file) will help more. – gabriel.hayes Nov 01 '19 at 19:03
0

First upload your font file onto the server, because not everyone has it.

@font-face {
  font-family: myFirstFont;
  src: url(css/fonts/customFont1.woff);
}

Then apply the JS

document.body.style.fontFamily = customFont1;

Or force apply to all elements

var style = document.createElement('style');
style.innerHTML = '* { font-family: customFont1 !important; }';
document.body.appendChild(style);

But it doesn't mean the setting will be remembered after the user refresh the page. You must implement something to remember the setting yourself.

Thanh Trung
  • 3,566
  • 3
  • 31
  • 42
  • Hey @Thanh Trung, thanks for this example. Your way is great but how do I make my own font family instead of Arial as you posted? – younyokel Nov 01 '19 at 19:24
  • Oh well, create your own font? Too difficult. I suggest you to use a public font https://fonts.google.com/ or embed an existing font to your site https://www.w3schools.com/css/css3_fonts.asp – Thanh Trung Nov 01 '19 at 22:36
  • I meant to choose my own font and set it instead of Arial (not creating it). – younyokel Nov 02 '19 at 07:58
  • Look at @Woodrow version. Pick a font using a select, then apply `document.body.style.fontFamily = 'myFontNameHere'`. Make sure to import the font using `font-face` if the font is not a common font that all PC have. – Thanh Trung Nov 02 '19 at 11:38