Last active
July 11, 2022 20:13
-
-
Save relliv/515a8e0abba0445c34f9c3511445426e to your computer and use it in GitHub Desktop.
Check given emoji is supporting by headless chromium
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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/[email protected]/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