├── .github
└── CODEOWNERS
├── .gitignore
├── .vercelignore
├── .yarnrc
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── api
├── _fonts
│ ├── Inter-Bold.woff2
│ ├── Inter-License.txt
│ ├── Inter-Regular.woff2
│ ├── Vera-License.txt
│ └── Vera-Mono.woff2
├── _lib
│ ├── chromium.ts
│ ├── options.ts
│ ├── parser.ts
│ ├── sanitizer.ts
│ ├── template.ts
│ └── types.ts
├── index.ts
└── tsconfig.json
├── package.json
├── public
├── archive.html
├── favicon.ico
├── robots.txt
├── style.css
└── tweet.png
├── vercel.json
├── web
├── archive.ts
└── tsconfig.json
└── yarn.lock
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @styfle
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .now
2 | .vercel
3 | node_modules
4 | api/dist
5 | public/dist
6 |
7 |
--------------------------------------------------------------------------------
/.vercelignore:
--------------------------------------------------------------------------------
1 | .github
2 | node_modules
3 | api/dist
4 | public/dist
5 | CONTRIBUTING.md
6 | README.md
7 |
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | save-prefix ""
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | There are three pieces to `og-image` that are worth noting before you begin development.
4 |
5 | 1. The backend image generator located in [/api/index.ts](https://github.com/vercel/og-image/blob/main/api/index.ts)
6 | 2. The html/css template used to generate the image is located in [/_lib/template.ts](https://github.com/vercel/og-image/blob/main/api/_lib/template.ts)
7 | 3. The frontend inputs located in [/web/index.ts](https://github.com/vercel/og-image/blob/main/web/index.ts)
8 |
9 | Vercel handles [routing](https://github.com/vercel/og-image/blob/main/vercel.json#L6) in an elegant way for us so deployment is easy.
10 |
11 | To start hacking, do the following:
12 |
13 | 1. Clone this repo with `git clone https://github.com/vercel/og-image`
14 | 2. Change directory with `cd og-image`
15 | 3. Run `yarn` or `npm install` to install all dependencies
16 | 4. Run locally with `vercel dev` and visit [localhost:3000](http://localhost:3000) (if nothing happens, run `npm install -g vercel`)
17 | 5. If necessary, edit the `exePath` in [options.ts](https://github.com/vercel/og-image/blob/main/api/_lib/options.ts) to point to your local Chrome executable
18 |
19 | Now you're ready to start local development!
20 |
21 | You can set an environment variable to assist with debugging `export OG_HTML_DEBUG=1`. This will render the image as HTML so you can play around with your browser's dev tools before committing changes to the template.
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019-2020 Vercel, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > **Warning** This repo is outdated and only works with Node.js 14. Please use [@vercel/og](https://vercel.com/blog/introducing-vercel-og-image-generation-fast-dynamic-social-card-images) for new projects.
2 | >
3 | > If you have a problem that reproduces using [the playground](https://og-playground.vercel.app), please create an issue in the [satori](https://github.com/vercel/satori) repo.
4 | >
5 | > For all other issues with `@vercel/og`, please reach out to [Vercel Support](https://vercel.com/help#issues).
6 |
--------------------------------------------------------------------------------
/api/_fonts/Inter-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel/og-image/d6c78472613b6f63e5c633555041a88385e632d0/api/_fonts/Inter-Bold.woff2
--------------------------------------------------------------------------------
/api/_fonts/Inter-License.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016-2018 The Inter Project Authors (me@rsms.me)
2 |
3 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
4 | This license is copied below, and is also available with a FAQ at:
5 | http://scripts.sil.org/OFL
6 |
7 | -----------------------------------------------------------
8 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
9 | -----------------------------------------------------------
10 |
11 | PREAMBLE
12 | The goals of the Open Font License (OFL) are to stimulate worldwide
13 | development of collaborative font projects, to support the font creation
14 | efforts of academic and linguistic communities, and to provide a free and
15 | open framework in which fonts may be shared and improved in partnership
16 | with others.
17 |
18 | The OFL allows the licensed fonts to be used, studied, modified and
19 | redistributed freely as long as they are not sold by themselves. The
20 | fonts, including any derivative works, can be bundled, embedded,
21 | redistributed and/or sold with any software provided that any reserved
22 | names are not used by derivative works. The fonts and derivatives,
23 | however, cannot be released under any other type of license. The
24 | requirement for fonts to remain under this license does not apply
25 | to any document created using the fonts or their derivatives.
26 |
27 | DEFINITIONS
28 | "Font Software" refers to the set of files released by the Copyright
29 | Holder(s) under this license and clearly marked as such. This may
30 | include source files, build scripts and documentation.
31 |
32 | "Reserved Font Name" refers to any names specified as such after the
33 | copyright statement(s).
34 |
35 | "Original Version" refers to the collection of Font Software components as
36 | distributed by the Copyright Holder(s).
37 |
38 | "Modified Version" refers to any derivative made by adding to, deleting,
39 | or substituting -- in part or in whole -- any of the components of the
40 | Original Version, by changing formats or by porting the Font Software to a
41 | new environment.
42 |
43 | "Author" refers to any designer, engineer, programmer, technical
44 | writer or other person who contributed to the Font Software.
45 |
46 | PERMISSION AND CONDITIONS
47 | Permission is hereby granted, free of charge, to any person obtaining
48 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
49 | redistribute, and sell modified and unmodified copies of the Font
50 | Software, subject to the following conditions:
51 |
52 | 1) Neither the Font Software nor any of its individual components,
53 | in Original or Modified Versions, may be sold by itself.
54 |
55 | 2) Original or Modified Versions of the Font Software may be bundled,
56 | redistributed and/or sold with any software, provided that each copy
57 | contains the above copyright notice and this license. These can be
58 | included either as stand-alone text files, human-readable headers or
59 | in the appropriate machine-readable metadata fields within text or
60 | binary files as long as those fields can be easily viewed by the user.
61 |
62 | 3) No Modified Version of the Font Software may use the Reserved Font
63 | Name(s) unless explicit written permission is granted by the corresponding
64 | Copyright Holder. This restriction only applies to the primary font name as
65 | presented to the users.
66 |
67 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
68 | Software shall not be used to promote, endorse or advertise any
69 | Modified Version, except to acknowledge the contribution(s) of the
70 | Copyright Holder(s) and the Author(s) or with their explicit written
71 | permission.
72 |
73 | 5) The Font Software, modified or unmodified, in part or in whole,
74 | must be distributed entirely under this license, and must not be
75 | distributed under any other license. The requirement for fonts to
76 | remain under this license does not apply to any document created
77 | using the Font Software.
78 |
79 | TERMINATION
80 | This license becomes null and void if any of the above conditions are
81 | not met.
82 |
83 | DISCLAIMER
84 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
85 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
86 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
87 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
88 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
89 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
90 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
91 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
92 | OTHER DEALINGS IN THE FONT SOFTWARE.
93 |
--------------------------------------------------------------------------------
/api/_fonts/Inter-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel/og-image/d6c78472613b6f63e5c633555041a88385e632d0/api/_fonts/Inter-Regular.woff2
--------------------------------------------------------------------------------
/api/_fonts/Vera-License.txt:
--------------------------------------------------------------------------------
1 | Bitstream Vera Fonts Copyright
2 |
3 | The fonts have a generous copyright, allowing derivative works (as
4 | long as "Bitstream" or "Vera" are not in the names), and full
5 | redistribution (so long as they are not *sold* by themselves). They
6 | can be be bundled, redistributed and sold with any software.
7 |
8 | The fonts are distributed under the following copyright:
9 |
10 | Copyright
11 | =========
12 |
13 | Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
14 | Vera is a trademark of Bitstream, Inc.
15 |
16 | Permission is hereby granted, free of charge, to any person obtaining
17 | a copy of the fonts accompanying this license ("Fonts") and associated
18 | documentation files (the "Font Software"), to reproduce and distribute
19 | the Font Software, including without limitation the rights to use,
20 | copy, merge, publish, distribute, and/or sell copies of the Font
21 | Software, and to permit persons to whom the Font Software is furnished
22 | to do so, subject to the following conditions:
23 |
24 | The above copyright and trademark notices and this permission notice
25 | shall be included in all copies of one or more of the Font Software
26 | typefaces.
27 |
28 | The Font Software may be modified, altered, or added to, and in
29 | particular the designs of glyphs or characters in the Fonts may be
30 | modified and additional glyphs or characters may be added to the
31 | Fonts, only if the fonts are renamed to names not containing either
32 | the words "Bitstream" or the word "Vera".
33 |
34 | This License becomes null and void to the extent applicable to Fonts
35 | or Font Software that has been modified and is distributed under the
36 | "Bitstream Vera" names.
37 |
38 | The Font Software may be sold as part of a larger software package but
39 | no copy of one or more of the Font Software typefaces may be sold by
40 | itself.
41 |
42 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
43 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
44 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
45 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
46 | BITSTREAM OR THE GNOME FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR
47 | OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL,
48 | OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR
49 | OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT
50 | SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
51 |
52 | Except as contained in this notice, the names of Gnome, the Gnome
53 | Foundation, and Bitstream Inc., shall not be used in advertising or
54 | otherwise to promote the sale, use or other dealings in this Font
55 | Software without prior written authorization from the Gnome Foundation
56 | or Bitstream Inc., respectively. For further information, contact:
57 | fonts at gnome dot org.
58 |
59 | Copyright FAQ
60 | =============
61 |
62 | 1. I don't understand the resale restriction... What gives?
63 |
64 | Bitstream is giving away these fonts, but wishes to ensure its
65 | competitors can't just drop the fonts as is into a font sale system
66 | and sell them as is. It seems fair that if Bitstream can't make money
67 | from the Bitstream Vera fonts, their competitors should not be able to
68 | do so either. You can sell the fonts as part of any software package,
69 | however.
70 |
71 | 2. I want to package these fonts separately for distribution and
72 | sale as part of a larger software package or system. Can I do so?
73 |
74 | Yes. A RPM or Debian package is a "larger software package" to begin
75 | with, and you aren't selling them independently by themselves.
76 | See 1. above.
77 |
78 | 3. Are derivative works allowed?
79 | Yes!
80 |
81 | 4. Can I change or add to the font(s)?
82 | Yes, but you must change the name(s) of the font(s).
83 |
84 | 5. Under what terms are derivative works allowed?
85 |
86 | You must change the name(s) of the fonts. This is to ensure the
87 | quality of the fonts, both to protect Bitstream and Gnome. We want to
88 | ensure that if an application has opened a font specifically of these
89 | names, it gets what it expects (though of course, using fontconfig,
90 | substitutions could still could have occurred during font
91 | opening). You must include the Bitstream copyright. Additional
92 | copyrights can be added, as per copyright law. Happy Font Hacking!
93 |
94 | 6. If I have improvements for Bitstream Vera, is it possible they might get
95 | adopted in future versions?
96 |
97 | Yes. The contract between the Gnome Foundation and Bitstream has
98 | provisions for working with Bitstream to ensure quality additions to
99 | the Bitstream Vera font family. Please contact us if you have such
100 | additions. Note, that in general, we will want such additions for the
101 | entire family, not just a single font, and that you'll have to keep
102 | both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
103 | glyphs to the font, they must be stylistically in keeping with Vera's
104 | design. Vera cannot become a "ransom note" font. Jim Lyles will be
105 | providing a document describing the design elements used in Vera, as a
106 | guide and aid for people interested in contributing to Vera.
107 |
108 | 7. I want to sell a software package that uses these fonts: Can I do so?
109 |
110 | Sure. Bundle the fonts with your software and sell your software
111 | with the fonts. That is the intent of the copyright.
112 |
113 | 8. If applications have built the names "Bitstream Vera" into them,
114 | can I override this somehow to use fonts of my choosing?
115 |
116 | This depends on exact details of the software. Most open source
117 | systems and software (e.g., Gnome, KDE, etc.) are now converting to
118 | use fontconfig (see www.fontconfig.org) to handle font configuration,
119 | selection and substitution; it has provisions for overriding font
120 | names and subsituting alternatives. An example is provided by the
121 | supplied local.conf file, which chooses the family Bitstream Vera for
122 | "sans", "serif" and "monospace". Other software (e.g., the XFree86
123 | core server) has other mechanisms for font substitution.
124 |
125 |
--------------------------------------------------------------------------------
/api/_fonts/Vera-Mono.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vercel/og-image/d6c78472613b6f63e5c633555041a88385e632d0/api/_fonts/Vera-Mono.woff2
--------------------------------------------------------------------------------
/api/_lib/chromium.ts:
--------------------------------------------------------------------------------
1 | import core from 'puppeteer-core';
2 | import { getOptions } from './options';
3 | import { FileType } from './types';
4 | let _page: core.Page | null;
5 |
6 | async function getPage(isDev: boolean) {
7 | if (_page) {
8 | return _page;
9 | }
10 | const options = await getOptions(isDev);
11 | const browser = await core.launch(options);
12 | _page = await browser.newPage();
13 | return _page;
14 | }
15 |
16 | export async function getScreenshot(html: string, type: FileType, isDev: boolean) {
17 | const page = await getPage(isDev);
18 | await page.setViewport({ width: 2048, height: 1170 });
19 | await page.setContent(html);
20 | const file = await page.screenshot({ type });
21 | return file;
22 | }
23 |
--------------------------------------------------------------------------------
/api/_lib/options.ts:
--------------------------------------------------------------------------------
1 | import chrome from 'chrome-aws-lambda';
2 | const exePath = process.platform === 'win32'
3 | ? 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
4 | : process.platform === 'linux'
5 | ? '/usr/bin/google-chrome'
6 | : '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
7 |
8 | interface Options {
9 | args: string[];
10 | executablePath: string;
11 | headless: boolean;
12 | }
13 |
14 | export async function getOptions(isDev: boolean) {
15 | let options: Options;
16 | if (isDev) {
17 | options = {
18 | args: [],
19 | executablePath: exePath,
20 | headless: true
21 | };
22 | } else {
23 | options = {
24 | args: chrome.args,
25 | executablePath: await chrome.executablePath,
26 | headless: chrome.headless,
27 | };
28 | }
29 | return options;
30 | }
31 |
--------------------------------------------------------------------------------
/api/_lib/parser.ts:
--------------------------------------------------------------------------------
1 | import { IncomingMessage } from 'http';
2 | import { parse } from 'url';
3 | import { ParsedRequest, Theme } from './types';
4 |
5 | export function parseRequest(req: IncomingMessage) {
6 | console.log('HTTP ' + req.url);
7 | const { pathname, query } = parse(req.url || '/', true);
8 | const { fontSize, images, widths, heights, theme, md } = (query || {});
9 |
10 | if (Array.isArray(fontSize)) {
11 | throw new Error('Expected a single fontSize');
12 | }
13 | if (Array.isArray(theme)) {
14 | throw new Error('Expected a single theme');
15 | }
16 |
17 | const arr = (pathname || '/').slice(1).split('.');
18 | let extension = '';
19 | let text = '';
20 | if (arr.length === 0) {
21 | text = '';
22 | } else if (arr.length === 1) {
23 | text = arr[0];
24 | } else {
25 | extension = arr.pop() as string;
26 | text = arr.join('.');
27 | }
28 |
29 | const parsedRequest: ParsedRequest = {
30 | fileType: extension === 'jpeg' ? extension : 'png',
31 | text: decodeURIComponent(text),
32 | theme: theme === 'dark' ? 'dark' : 'light',
33 | md: md === '1' || md === 'true',
34 | fontSize: fontSize || '96px',
35 | images: getArray(images),
36 | widths: getArray(widths),
37 | heights: getArray(heights),
38 | };
39 | parsedRequest.images = getDefaultImages(parsedRequest.images, parsedRequest.theme);
40 | return parsedRequest;
41 | }
42 |
43 | function getArray(stringOrArray: string[] | string | undefined): string[] {
44 | if (typeof stringOrArray === 'undefined') {
45 | return [];
46 | } else if (Array.isArray(stringOrArray)) {
47 | return stringOrArray;
48 | } else {
49 | return [stringOrArray];
50 | }
51 | }
52 |
53 | function getDefaultImages(images: string[], theme: Theme): string[] {
54 | const defaultImage = theme === 'light'
55 | ? 'https://assets.vercel.com/image/upload/front/assets/design/vercel-triangle-black.svg'
56 | : 'https://assets.vercel.com/image/upload/front/assets/design/vercel-triangle-white.svg';
57 |
58 | if (!images || !images[0]) {
59 | return [defaultImage];
60 | }
61 | if (!images[0].startsWith('https://assets.vercel.com/') && !images[0].startsWith('https://assets.zeit.co/')) {
62 | images[0] = defaultImage;
63 | }
64 | return images;
65 | }
66 |
--------------------------------------------------------------------------------
/api/_lib/sanitizer.ts:
--------------------------------------------------------------------------------
1 | const entityMap: { [key: string]: string } = {
2 | "&": "&",
3 | "<": "<",
4 | ">": ">",
5 | '"': '"',
6 | "'": ''',
7 | "/": '/'
8 | };
9 |
10 | export function sanitizeHtml(html: string) {
11 | return String(html).replace(/[&<>"'\/]/g, key => entityMap[key]);
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/api/_lib/template.ts:
--------------------------------------------------------------------------------
1 |
2 | import { readFileSync } from 'fs';
3 | import { sanitizeHtml } from './sanitizer';
4 | import { ParsedRequest } from './types';
5 | const twemoji = require('twemoji');
6 | const twOptions = { folder: 'svg', ext: '.svg' };
7 | const emojify = (text: string) => twemoji.parse(text, twOptions);
8 |
9 | const rglr = readFileSync(`${__dirname}/../_fonts/Inter-Regular.woff2`).toString('base64');
10 | const bold = readFileSync(`${__dirname}/../_fonts/Inter-Bold.woff2`).toString('base64');
11 | const mono = readFileSync(`${__dirname}/../_fonts/Vera-Mono.woff2`).toString('base64');
12 |
13 | function getCss(theme: string, fontSize: string) {
14 | let background = 'white';
15 | let foreground = 'black';
16 | let radial = 'lightgray';
17 |
18 | if (theme === 'dark') {
19 | background = 'black';
20 | foreground = 'white';
21 | radial = 'dimgray';
22 | }
23 | return `
24 | @font-face {
25 | font-family: 'Inter';
26 | font-style: normal;
27 | font-weight: normal;
28 | src: url(data:font/woff2;charset=utf-8;base64,${rglr}) format('woff2');
29 | }
30 |
31 | @font-face {
32 | font-family: 'Inter';
33 | font-style: normal;
34 | font-weight: bold;
35 | src: url(data:font/woff2;charset=utf-8;base64,${bold}) format('woff2');
36 | }
37 |
38 | @font-face {
39 | font-family: 'Vera';
40 | font-style: normal;
41 | font-weight: normal;
42 | src: url(data:font/woff2;charset=utf-8;base64,${mono}) format("woff2");
43 | }
44 |
45 | body {
46 | background: ${background};
47 | background-image: radial-gradient(circle at 25px 25px, ${radial} 2%, transparent 0%), radial-gradient(circle at 75px 75px, ${radial} 2%, transparent 0%);
48 | background-size: 100px 100px;
49 | height: 100vh;
50 | display: flex;
51 | text-align: center;
52 | align-items: center;
53 | justify-content: center;
54 | }
55 |
56 | code {
57 | color: #D400FF;
58 | font-family: 'Vera';
59 | white-space: pre-wrap;
60 | letter-spacing: -5px;
61 | }
62 |
63 | code:before, code:after {
64 | content: '\`';
65 | }
66 |
67 | .logo-wrapper {
68 | display: flex;
69 | align-items: center;
70 | align-content: center;
71 | justify-content: center;
72 | justify-items: center;
73 | }
74 |
75 | .logo {
76 | margin: 0 75px;
77 | }
78 |
79 | .plus {
80 | color: #BBB;
81 | font-family: Times New Roman, Verdana;
82 | font-size: 100px;
83 | }
84 |
85 | .spacer {
86 | margin: 150px;
87 | }
88 |
89 | .emoji {
90 | height: 1em;
91 | width: 1em;
92 | margin: 0 .05em 0 .1em;
93 | vertical-align: -0.1em;
94 | }
95 |
96 | .heading {
97 | font-family: 'Inter', sans-serif;
98 | font-size: ${sanitizeHtml(fontSize)};
99 | font-style: normal;
100 | color: ${foreground};
101 | line-height: 1.8;
102 | }`;
103 | }
104 |
105 | export function getHtml(parsedReq: ParsedRequest) {
106 | const { text, theme, md, fontSize, images, widths, heights } = parsedReq;
107 | let html = sanitizeHtml(text);
108 | if (md) {
109 | html = html.replace(/\*\*(.+)\*\*/g, (_, match) => `${match}`)
110 | html = html.replace(/__(.+)__/g, (_, match) => `${match}`)
111 | html = html.replace(/\*(.+)\*/g, (_, match) => `${match}`)
112 | html = html.replace(/_(.+)_/g, (_, match) => `${match}`)
113 | }
114 | return `
115 |
116 |
117 |