Skip to content

Instantly share code, notes, and snippets.

@relliv
Last active July 11, 2022 20:13
Show Gist options
  • Save relliv/515a8e0abba0445c34f9c3511445426e to your computer and use it in GitHub Desktop.
Save relliv/515a8e0abba0445c34f9c3511445426e to your computer and use it in GitHub Desktop.
Check given emoji is supporting by headless chromium
/* eslint-disable no-var */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { HttpService } from '@nestjs/axios';
import { Controller, Get, Param } from '@nestjs/common';
import { HTMLElement, Node, NodeType, parse } from 'node-html-parser';
import { catchError, map, Observable, of } from 'rxjs';
import puppeteer from 'puppeteer';
@Controller('unicode')
export class UnicodeController {
private maximumEmojiVersion = 13;
constructor(private readonly httpService: HttpService) {}
@Get('version/:version')
async getVersion(@Param('version') version): Promise<Observable<any>> {
return await this.httpService
.get(`https://emojipedia.org/unicode-${version}/`)
.pipe(
map(async (res) => {
const parsedHtml = parse(res.data);
const unicodeEmojiItems = parsedHtml.querySelectorAll(
'div.content>article>ul:nth-child(3) li>a',
);
let unicodeEmojiList = null;
if (unicodeEmojiItems && unicodeEmojiItems) {
const browser = await puppeteer.launch(),
page = await browser.newPage();
unicodeEmojiList = unicodeEmojiItems
.map((element: HTMLElement) => {
if (element.attributes && element.attributes.href) {
const versionData = element.innerText.split(' ');
const item = {
emoji: versionData[0]?.trim() || null,
page: element.attributes.href,
};
return item;
}
})
.filter((x) => x.emoji);
for (let i = 0; i < unicodeEmojiList.length; i++) {
const item = unicodeEmojiList[i];
// Get the "viewport" of the page, as reported by the page.
const isEmojiSupported = await page.evaluate((emoji) => {
// this part is to check if the given emoji is supported by chromium or not
// source code: https://github.com/koala-interactive/is-emoji-supported
// umd source: https://egoistdeveloper.github.io/npm-explorer/?p=is-emoji-supported@0.0.5/dist/cjs/is-emoji-supported.js&selection=13:18-13:18
var cache = new Map();
function isEmojiSupported(unicode) {
if (cache.has(unicode)) {
return cache.get(unicode);
}
var supported = isSupported(unicode);
cache.set(unicode, supported);
return supported;
}
var isSupported = (function () {
var ctx = null;
try {
ctx = document.createElement('canvas').getContext('2d');
} catch (_a) {}
// Not in browser env
if (!ctx) {
return function () {
return false;
};
}
var CANVAS_HEIGHT = 25,
CANVAS_WIDTH = 20,
textSize = Math.floor(CANVAS_HEIGHT / 2);
// Initialize convas context
ctx.font = textSize + 'px Arial, Sans-Serif';
ctx.textBaseline = 'top';
ctx.canvas.width = CANVAS_WIDTH * 2;
ctx.canvas.height = CANVAS_HEIGHT;
return function (unicode) {
ctx.clearRect(0, 0, CANVAS_WIDTH * 2, CANVAS_HEIGHT);
// Draw in red on the left
ctx.fillStyle = '#FF0000';
ctx.fillText(unicode, 0, 22);
// Draw in blue on right
ctx.fillStyle = '#0000FF';
ctx.fillText(unicode, CANVAS_WIDTH, 22);
var a = ctx.getImageData(
0,
0,
CANVAS_WIDTH,
CANVAS_HEIGHT,
).data;
var count = a.length,
i = 0;
// Search the first visible pixel
for (; i < count && !a[i + 3]; i += 4);
// No visible pixel
if (i >= count) {
return false;
}
// Emoji has immutable color, so we check the color of the emoji in two different colors
// the result show be the same.
var x = CANVAS_WIDTH + ((i / 4) % CANVAS_WIDTH),
y = Math.floor(i / 4 / CANVAS_WIDTH),
b = ctx.getImageData(x, y, 1, 1).data;
if (a[i] !== b[0] || a[i + 2] !== b[2]) {
return false;
}
// Some emojis are a contraction of different ones, so if it's not
// supported, it will show multiple characters
if (ctx.measureText(unicode).width >= CANVAS_WIDTH) {
return false;
}
// Supported
return true;
};
})();
return {
isSupported: isEmojiSupported(emoji),
};
}, item.emoji);
unicodeEmojiList[i].isEmojiSupported =
isEmojiSupported.isSupported;
}
await browser.close();
}
// await page.goto('https://example.com');
return (
'<a href="../versions">Return Back</a>' +
`<h3>Unicode Emojis for v${version} (${unicodeEmojiList.length})</h3> <div style="font-size: 40px;">` +
unicodeEmojiList
.map(
(item: any) =>
`<a href="emoji/${item.page}" style="margin: 10px;">${
item.emoji
} ${item.isEmojiSupported ? '' : '❌'}</a>`,
)
.join('') +
'</div>'
);
return res.data;
}),
catchError((error) =>
of({ message: error.message, status: error.status }),
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment