Skip to content

Commit

Permalink
Options for font, terminal-bg, line-break.
Browse files Browse the repository at this point in the history
Sanitized title text
Proper vertical align for title
Updated default background gradient
  • Loading branch information
manustays committed Jan 29, 2021
1 parent 38a47a6 commit 5f19d97
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 17 deletions.
12 changes: 9 additions & 3 deletions .eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ const defaults = {
titleColor: '#FFF',
hideTerminal: false,
bgColor: '',
customSVG: ''
terminalBgColor: '#404040',
customSVG: '',
customFontFilename: '',
lineBreakAt: 35
};

module.exports = (eleventyConfig, options) => {

// Combine defaults with user defined options
const { outputDir, urlPath, titleColor, siteName, promoImage, hideTerminal, bgColor, customSVG } = { ...defaults, ...options };
const { outputDir, urlPath, titleColor, siteName, promoImage, hideTerminal, bgColor, terminalBgColor, customSVG, customFontFilename, lineBreakAt } = { ...defaults, ...options };

eleventyConfig.addAsyncShortcode("GenerateSocialImage", async (title) => {
if (!title) return '';
Expand All @@ -32,7 +35,10 @@ module.exports = (eleventyConfig, options) => {
titleColor,
hideTerminal,
bgColor,
customSVG
terminalBgColor,
customSVG,
customFontFilename,
lineBreakAt
}
);
});
Expand Down
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ I created a new plugin because the above mentioned plugin...
* uses Puppeteer to generate the image from a webpage.
* I faced some issues running Puppeteer on WSL2, so decided to get rid of the dependency.
* uses a separate build process to generate the images.
* While it is totally fine (even better as it can be used with any other SSG), I wanted the workflow within the Eleventy build process, i.e, by using an Eleventy ShortCode.
* While it is totally fine (even better, as it can be used with any other SSG), I wanted the workflow within the Eleventy build process, i.e, by using an Eleventy ShortCode.

## How does it work?
* Generates the image using SVG and then converts it into PNG using [Sharp](https://github.com/lovell/sharp).
Expand Down Expand Up @@ -69,13 +69,34 @@ For example, in your `base.njk` template file, use it in the `<head>` for genera
| siteName | string | | The website name to show on the social-image |
| titleColor | string | "white" | The color of the page-title |
| bgColor | string | | Optional background color. Otherwise, shows the gradient pattern |
| hideTerminal | boolean | false | If true, hides the terminal window design behind the title. |
| terminalBgColor| string | "#404040" | Background color of the terminal window design |
| hideTerminal | boolean | false | If true, hides the terminal window design behind the title |
| customSVG | string | | Custom SVG code to be added to the image. Use this to add your own design or text anywhere on the image |
| customFontFilename | string | | Filename of custom local font used for title ([see **Custom Fonts**](#custom-fonts)) |
| lineBreakAt | number | 35 | Maximum row length for wrapping the title. Required because SVG does not have auto-wrapping text. Should depends on the font used |

## Custom Fonts
The [Sharp](https://github.com/lovell/sharp) library uses librsvg that uses [fontconfig](https://www.freedesktop.org/software/fontconfig/fontconfig-user) to load external fonts. Therefore the following steps are required:
1. Download your font file in project sub-folder. Eg: `./fonts/sans.ttf`
2. Create a file `fonts.conf` with the following content:
```xml
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir prefix="default">fonts</dir>
</fontconfig>
```
3. Setup the following environment variable on your build server (eg: Netlify):
```bash
FONTCONFIG_PATH=.
```


## TODO
- [ ] Cache result to avoid regenerating same image.
- [*] Better text-wrap logic for the page-title in SVG.
- [ ] Cache result to avoid regenerating same image
- [*] Better text-wrap logic for the page-title in SVG
- [*] Custom SVG
- [*] Custom font
- [ ] More customization options!


Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@manustays/eleventy-plugin-generate-social-images",
"version": "3.1.0",
"version": "4.0.0",
"description": "Dynamically generate social media images for your Eleventy blog pages.",
"homepage": "",
"main": ".eleventy.js",
Expand Down
45 changes: 37 additions & 8 deletions utils/generateSocialImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,23 @@ function wrapTitle(title, rowLength, maxRows)
}


/**
* Sanitize text for embedding into XML/HTML.
* @param {*} text The text to sanitize
*/
function sanitizeHTML(text) {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}



async function generateSocialImage(filename, title, siteName, authorImage, options = {}) {
const { outputDir, urlPath, titleColor, hideTerminal, bgColor, customSVG } = options;
const { outputDir, urlPath, titleColor, hideTerminal, bgColor, terminalBgColor, customSVG, customFontFilename, lineBreakAt } = options;

if (!(title && outputDir && urlPath)) {
console.error("eleventy-plugin-generate-social-images Error: Missing values");
Expand All @@ -55,7 +69,7 @@ async function generateSocialImage(filename, title, siteName, authorImage, optio


// Generate multi-line SVG text for the Title...
const line_length = 35;
const line_length = lineBreakAt;
const max_lines = 4;
const start_x = 150;
const start_y = 210;
Expand All @@ -64,11 +78,23 @@ async function generateSocialImage(filename, title, siteName, authorImage, optio

let title_rows = wrapTitle(title, line_length, max_lines);

const start_y_middle = start_y + (((max_lines - title_rows.length) * line_height) / 3);

const svgTitle = title_rows.reduce((p, c, i) => {
return p + `<text x="${start_x}" y="${start_y + (i * line_height)}" fill="${titleColor}" font-size="${font_size}px" font-weight="700">${c}</text>`;
c = sanitizeHTML(c);
return p + `<text x="${start_x}" y="${start_y_middle + (i * line_height)}" fill="${titleColor}" font-size="${font_size}px" font-weight="700">${c}</text>`;
}, '');

const terminalWindow = `<rect x="100" y="64" width="1000" height="500" rx="16" ry="16" fill="#404040" stroke-width="1" stroke="#aaa" />
let customFont = '';
if (customFontFilename) {
customFont = `@font-face {
font-family: 'cust';
font-style: 'normal';
src: url("${customFontFilename}");
}`;
}

const terminalWindow = `<rect x="100" y="64" width="1000" height="500" rx="16" ry="16" fill="${terminalBgColor}" stroke-width="1" stroke="#aaa" />
<circle cx="135" cy="100" r="12" fill="#FD5454" />
<circle cx="170" cy="100" r="12" fill="#F6B23C" />
<circle cx="205" cy="100" r="12" fill="#22C036" />`;
Expand All @@ -77,9 +103,12 @@ async function generateSocialImage(filename, title, siteName, authorImage, optio
let template = `<svg width="1200" height="628" viewbox="0 0 1200 628" xmlns="http://www.w3.org/2000/svg">
<defs>
<style>
${customFont}
</style>
<linearGradient id="the-gradient" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stop-color="#970069" />
<stop offset="100%" stop-color="#4D7cac" />
<stop offset="0%" stop-color="#647DEE" />
<stop offset="100%" stop-color="#7F53AC" />
</linearGradient>
</defs>
Expand All @@ -89,7 +118,7 @@ async function generateSocialImage(filename, title, siteName, authorImage, optio
${customSVG}
<g style="font-family:sans-serif">
<g style="font-family:'cust',sans-serif">
${svgTitle}
<text x="265" y="500" fill="#fff" font-size="30px" font-weight="700">${siteName}</text>
</g>
Expand All @@ -112,7 +141,7 @@ async function generateSocialImage(filename, title, siteName, authorImage, optio
.png()
.toFile(`${targetDir}/${filename}.png`);
} catch (err) {
console.error("eleventy-plugin-generate-social-images Error: ", err);
console.error("eleventy-plugin-generate-social-images Error: ", err, { template, filename, title, siteName, authorImage, options } );
return '';
}

Expand Down

0 comments on commit 5f19d97

Please sign in to comment.