Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Jitai (字体): A fairly full-featured font randomizer for WaniKani.

List of supported fonts and download links

Fonts from freejapanesefont.com

I would recommend taking a look at these fonts, and installing the ones you'd like. Once installed, Jitai will automatically use them. I, personally, have them all installed - it's a good mix!

Jitai will only use any given font for words where it has all the kanji in it, so don't be afraid of installing fonts that only contain a limited subset of kanji - it can't hurt. Go wild!


Default Windows fonts

  • Meiryo
  • MS Gothic
  • MS Mincho
  • Yu Gothic
  • Yu Mincho

Default OS X fonts

  • Hiragino Kaku Gothic Pro
  • Hiragino Maru Gothic Pro
  • Hiragino Mincho Pro

Common Linux fonts

EPSON families (download and run the .exe)

  • EPSON 行書体M
  • EPSON 正楷書体M
  • EPSON 教科書体M
  • EPSON 太明朝体B
  • EPSON 太行書体B
  • EPSON 丸ゴシック体M

Other fonts (Typekit, etc.)

// ==UserScript==
// @name Jitai
// @version 1.3.2
// @description Display WaniKani reviews in randomized fonts, for more varied reading training.
// @author Samuel (@obskyr)
// @copyright 2016-2018, obskyr
// @license MIT
// @namespace http://obskyr.io/
// @homepageURL https://gist.github.com/obskyr/9f3c77cf6bf663792c6e
// @icon http://i.imgur.com/qyuR9bD.png
// @include /^https?://(www\.)?wanikani\.com/review/session/?$/
// @grant none
// ==/UserScript==
/*
To control which fonts to choose from, edit this list.
If you feel too many fonts of a certain type are showing
up, remove a few of those from the list. If you've got
fonts that aren't in the list that you'd like to be used,
add their names and they'll be in the rotation.
*/
var fonts = [
// Default Windows fonts
"Meiryo, メイリオ",
"MS PGothic, MS Pゴシック, MS Gothic, MS ゴック",
"MS PMincho, MS P明朝, MS Mincho, MS 明朝",
"Yu Gothic, YuGothic",
"Yu Mincho, YuMincho",
// Default OS X fonts
"Hiragino Kaku Gothic Pro, ヒラギノ角ゴ Pro W3",
"Hiragino Maru Gothic Pro, ヒラギノ丸ゴ Pro W3",
"Hiragino Mincho Pro, ヒラギノ明朝 Pro W3",
// Common Linux fonts
"Takao Gothic, TakaoGothic",
"Takao Mincho, TakaoMincho",
"Sazanami Gothic",
"Sazanami Mincho",
"Kochi Gothic",
"Kochi Mincho",
"Dejima Mincho",
"Ume Gothic",
"Ume Mincho",
// Other Japanese fonts people use.
// You might want to try some of these!
"EPSON 行書体M",
"EPSON 正楷書体M",
"EPSON 教科書体M",
"EPSON 太明朝体B",
"EPSON 太行書体B",
"EPSON 丸ゴシック体M",
"cinecaption",
"nagayama_kai",
"A-OTF Shin Maru Go Pro",
"Hosofuwafont",
"ChihayaGothic",
"'chifont+', chifont",
"darts font",
"santyoume-font",
"FC-Flower",
"ArmedBanana", // This one is completely absurd. I recommend it.
"HakusyuKaisyoExtraBold_kk",
"aoyagireisyosimo2, AoyagiKouzanFont2OTF",
"aquafont",
// Add your fonts here!
"Fake font name that you can change",
"Another fake font name",
"Just add them like this!",
"Quotes around the name, comma after."
];
var existingFonts = [];
for (var i = 0; i < fonts.length; i++) {
var fontName = fonts[i];
if (fontExists(fontName)) {
existingFonts.push(fontName);
}
}
function fontExists(fontName) {
// Approach from kirupa.com/html5/detect_whether_font_is_installed.htm - thanks!
// Will return false for the browser's default monospace font, sadly.
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var text = "wim-—l~ツ亻".repeat(100); // Characters with widths that often vary between fonts.
context.font = "72px monospace";
var defaultWidth = context.measureText(text).width;
// Microsoft Edge raises an error when a context's font is set to a string
// containing certain special characters... so that needs to be handled.
try {
context.font = "72px " + fontName + ", monospace";
} catch (e) {
return false;
}
var testWidth = context.measureText(text).width;
return testWidth != defaultWidth;
}
function canRepresentGlyphs(fontName, glyphs) {
var canvas = document.createElement('canvas');
canvas.width = 50;
canvas.height = 50;
var context = canvas.getContext("2d");
context.textBaseline = 'top';
var blank = document.createElement('canvas');
blank.width = canvas.width;
blank.height = canvas.height;
var blankDataUrl = blank.toDataURL();
context.font = "24px " + fontName;
var result = true;
for (var i = 0; i < glyphs.length; i++) {
context.fillText(glyphs[i], 0, 0);
if (canvas.toDataURL() === blankDataUrl) {
result = false;
break;
}
context.clearRect(0, 0, canvas.width, canvas.height);
}
return result;
}
var jitai = {
setToRandomFont: function(glyphs) {
// The font is set as a randomly shuffled list of the existing fonts
// in order to always show a random font, even if the first one chosen
// doesn't have a certain glyph being attempted to be displayed.
var randomlyOrdered = this.getShuffledFonts();
// Some fonts don't contain certain radicals, for example, so it's best
// to check that the font used can represent all the glyphs. The reason
// the browser can't switch automatically is that some fonts report that
// they have a glyph, when in fact they just show up blank.
var currentFont;
if (glyphs) {
for (var i = 0; i < randomlyOrdered.length; i++) {
var fontName = randomlyOrdered[i];
if (canRepresentGlyphs(fontName, glyphs)) {
currentFont = fontName;
break;
}
}
} else {
currentFont = randomlyOrdered.join(', ');
}
this.currentFont = currentFont;
jitai.setHoverFont(jitai.defaultFont);
this.$characterSpan.css('font-family', currentFont);
},
setToDefaultFont: function() {
jitai.setHoverFont(jitai.currentFont);
this.$characterSpan.css('font-family', '');
},
setHoverFont: function(fontName) {
this.$hoverStyle.text("#character span:hover {font-family: " + fontName + " !important;}");
},
getShuffledFonts: function() {
// This shouldn't have to be part of the Jitai object,
// but it uses Jitai's local copy of Math.random, so
// this is pretty much the most reasonable way to do it.
var fonts = existingFonts.slice();
for (var i = fonts.length; i > 0;) {
var otherIndex = Math.floor(this.random() * i);
i--;
var temp = fonts[i];
fonts[i] = fonts[otherIndex];
fonts[otherIndex] = temp;
}
return fonts;
},
init: function() {
// Reorder scripts seem to like overwriting Math.random(!?), so this
// workaround is required for Jitai to work in conjunction with them.
var iframe = document.createElement('iframe');
iframe.className = 'jitai-workaround-for-reorder-script-compatibility';
iframe.style.display = 'none';
document.body.appendChild(iframe);
this.random = iframe.contentWindow.Math.random;
this.$characterSpan = $('#character span');
this.defaultFont = this.$characterSpan.css('font-family');
this.$hoverStyle = $('<style/>', {'type': 'text/css'});
$('head').append(this.$hoverStyle);
// answerChecker.evaluate is only called when checking the answer, which
// is why we catch it, check for the "proceed to correct/incorrect display"
// condition, and set the font back to default if it's a non-stopping answer.
var oldEvaluate = answerChecker.evaluate;
answerChecker.evaluate = function(questionType, answer) {
var result = oldEvaluate.apply(this, [questionType, answer]);
if (!result.exception) {
jitai.setToDefaultFont();
}
return result;
};
// $.jStorage.set('currentItem') is only called right when switching to a
// new question, which is why we hook into it to randomize the font at the
// exact right time: when a new item shows up.
var oldSet = $.jStorage.set;
$.jStorage.set = function(key, value, options) {
var ret = oldSet.apply(this, [key, value, options]);
if (key === 'currentItem') {
jitai.setToRandomFont(value.kan || value.voc || value.rad);
}
return ret;
};
}
};
$(document).ready(function() {
jitai.init();
// Make sure page doesn't jump around on hover.
var $heightStyle = $('<style/>', {'type': 'text/css'});
var heightCss = "";
// The different heights here are equal to the different line-heights.
heightCss += "#question #character {height: 1.6em;}";
heightCss += "#question #character.vocabulary {height: 3.21em;}";
heightCss += "@media (max-width: 767px) {";
heightCss += " #question #character {height: 2.4em;}";
heightCss += " #question #character.vocabulary {height: 4.85em;}";
heightCss += "}";
$heightStyle.text(heightCss);
$('head').append($heightStyle);
});
@logic

This comment has been minimized.

Copy link

commented May 7, 2019

Would love to see the name of the font (maybe small and unobtrusive, in light text, somewhere off in a corner) when an alternate font has been selected. Some of the fonts you mentioned in the readme are...wild. :)

But regardless: this script has been invaluable for training my eyes to see the different strokes of a character rather than a specific font's idea what it should look like; thank you so much for making this available!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.