Repository: jlengstorf/get-share-image Branch: main Commit: 8c03adbf74ef Files: 6 Total size: 12.7 KB Directory structure: gitextract_q870h7iy/ ├── .gitignore ├── .prettierrc ├── LICENSE ├── README.md ├── package.json └── src/ └── index.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules index.js index.d.ts index.mjs index.cjs index.cjs.* index.umd.* index.esm.* index.js.map ================================================ FILE: .prettierrc ================================================ { "endOfLine": "lf", "semi": true, "singleQuote": true, "tabWidth": 2, "trailingComma": "all" } ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2020 Jason Lengstorf Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Generate Social Media Images Using Cloudinary This is a utility function that builds social media images by overlaying a title and tagline over an image using [Cloudinary’s APIs](https://cloudinary.com/documentation/image_transformations?ap=lwj#adding_text_captions). > **NOTE:** a Cloudinary account is required to use this package. The free tier should be more than enough for most small to medium websites using this package. [Sign up for an account here!](https://jason.energy/cloudinary) **This was created as part of an article series:** - [How to design a social sharing card template](https://www.learnwithjason.dev/blog/design-social-sharing-card) - [How the code in this package works](https://www.learnwithjason.dev/blog/auto-generate-social-image) ## Installation ```bash # install using npm npm install --save @jlengstorf/get-share-image # install using yarn yarn add @jlengstorf/get-share-image ``` See how this is used in a production site in the [learnwithjason.dev source code](https://github.com/jlengstorf/learnwithjason.dev/blob/070468828e8c758d150a8d573fd471d786278243/packages/%40jlengstorf/gatsby-theme-code-blog/src/gatsby-theme-blog-core/components/post.js#L55-L64). ## Example Usage ```js import getShareImage from '@jlengstorf/get-share-image'; const socialImage = getShareImage({ title: 'Deploy a Node.js App to DigitalOcean with SSL', tagline: '#devops #nodejs #ssl', cloudName: 'jlengstorf', imagePublicID: 'lwj/blog-post-card', titleFont: 'futura', taglineFont: 'futura', textColor: '232129', }); ``` This generates an image URL: ```text https://res.cloudinary.com/jlengstorf/image/upload/w_1280,h_669,c_fill,q_auto,f_auto/w_760,c_fit,co_rgb:232129,g_south_west,x_480,y_254,l_text:futura_64:Deploy%20a%20Node.js%20App%20to%20DigitalOcean%20with%20SSL/w_760,c_fit,co_rgb:232129,g_north_west,x_480,y_445,l_text:futura_48:%23devops%20%23nodejs%20%23ssl/lwj/blog-post-card ``` Which looks like this: ![Deploy a Node.js App to DigitalOcean with SSL, from learnwithjason.dev](https://res.cloudinary.com/jlengstorf/image/upload/w_1280,h_669,c_fill,q_auto,f_auto/w_760,c_fit,co_rgb:232129,g_south_west,x_480,y_254,l_text:futura_64:Deploy%20a%20Node.js%20App%20to%20DigitalOcean%20with%20SSL/w_760,c_fit,co_rgb:232129,g_north_west,x_480,y_445,l_text:futura_48:%23devops%20%23nodejs%20%23ssl/lwj/blog-post-card) ## Options This utility function accepts a config object. Available options are as follows: | name | required | description | | ------------------ | -------- | -------------------------------------------------------------------- | | title | true | (string) title text to be placed on the card | | tagline | | (string) tagline text to be placed on the card | | cloudName | true | (string) your Cloudinary cloud name (i.e. your username) | | imagePublicID | true | (string) the public ID of your social image template | | cloudinaryUrlBase | | (string, default `https://res.cloudinary.com`) Cloudinary asset URL | | titleFont | | (string, default `arial`) font to use for rendering title | | titleExtraConfig | | (string) optional additional text overlay config | | taglineExtraConfig | | (string) optional additional text overlay config | | taglineFont | | (string, default `arial`) font to use for rendering tagline | | imageWidth | | (number, default `1280`) SEO image width (defaults to Twitter ratio) | | imageHeight | | (number, default `669`) SEO image height (defaults to Twitter ratio) | | textAreaWidth | | (number, default `760`) width of title and tagline text areas | | textLeftOffset | | (number, default `480`) distance from left edge to start text boxes | | titleGravity | | (string, default `south_west`) location the title is anchored from | | taglineGravity | | (string, default `north_west`) location the tagline is anchored from | | titleLeftOffset | | (number, `null`) distance from left edge to start text boxes | | taglineLeftOffset | | (number, default `null`) distance from left edge to start text boxes | | titleBottomOffset | | (number, default `254`) distance from bottom to start title text | | taglineTopOffset | | (number, default `445`) distance from top to start tagline text | | textColor | | (string, default `000000`) hex value for text color | | titleColor | | (string) hex value specific for title color. If this is not set, the color will be the one set to `textColor` | | taglineColor | | (string) hex value specific for tagline color. If this is not set, the color will be the one set to `textColor` | | titleFontSize | | (number, default `64`) font size to use for the title | | taglineFontSize | | (number, default `48`) font size to use for the tagline | | version | | (string) optional version string for caching | ### Setting config options ```js const socialImage = getShareImage({ title: 'My Post Title', tagline: 'A tagline for the post', cloudName: 'myusername', imagePublicID: 'my-template-image.jpg', titleExtraConfig: '_bold', // optional - set title font weight to bold textColor: '663399', // optional — set the color to purple }); ``` ## Who is using this? - [Echobind](https://echobind.com/) with their [blog image generator](https://github.com/echobind/blog-image-generator) - [@jsjoeio](https://github.com/jsjoeio) on his [personal website](https://github.com/jsjoeio/joeprevite.com) - [Horacio Herrera](https://horacioh.com) - [@chris_berry](https://twitter.com/chris_berry) on his [blog](https://chrisberry.io) - [@codebender828](https://twitter.com/codebender828) on his [blog](https://jbakebwa.dev) - [@ryan_c_harris](https://twitter.com/ryan_c_harris) on his [blog](https://ryanharris.dev) - [Idiomatic Programmers](https://idiomaticprogrammers.com/) ================================================ FILE: package.json ================================================ { "name": "@jlengstorf/get-share-image", "type": "module", "version": "1.0.1", "source": "src/index.ts", "exports": { "import": "./dist/index.js", "require": "./dist/index.cjs", "types": "./index.d.ts" }, "main": "./dist/index.cjs", "module": "./dist/index.esm.js", "unpkg": "./dist/index.umd.js", "author": "Jason Lengstorf (https://lengstorf.com)", "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/jlengstorf/get-share-image.git" }, "scripts": { "build": "microbundle", "dev": "microbundle watch", "prepublish": "npm run build" }, "files": [ "./dist" ], "types": "dist/index.d.ts", "devDependencies": { "microbundle": "^0.15.1", "typescript": "^5.3.3" } } ================================================ FILE: src/index.ts ================================================ type Gravity = | 'north_east' | 'north' | 'north_west' | 'west' | 'south_west' | 'south' | 'south_east' | 'east' | 'center'; type GetShareImageConfig = { title: string; tagline?: string; cloudName: string; imagePublicID: string; /** * Only needed if you have a custom Cloudinary URL * @default 'https://res.cloudinary.com' */ cloudinaryUrlBase?: string; /** * @default 'arial' */ titleFont?: string; titleExtraConfig?: string; taglineExtraConfig?: string; /** * @default 'arial' */ taglineFont?: string; /** * @default 1280 */ imageWidth?: number; /** * @default 669 */ imageHeight?: number; /** * @default 760 */ textAreaWidth?: number; /** * @default 480 */ textLeftOffset?: number; /** * @default 'south_west' */ titleGravity?: | 'north_east' | 'north' | 'north_west' | 'west' | 'south_west' | 'south' | 'south_east' | 'east' | 'center'; /** * @default 'north_west' */ taglineGravity?: | 'north_east' | 'north' | 'north_west' | 'west' | 'south_west' | 'south' | 'south_east' | 'east' | 'center'; titleLeftOffset?: number | null; taglineLeftOffset?: number | null; /** * @default 254 */ titleBottomOffset?: number; /** * @default 445 */ taglineTopOffset?: number; /** * @default '000000' */ textColor?: string; titleColor: string; taglineColor: string; /** * @default 64 */ titleFontSize?: number; /** * @default 48 */ taglineFontSize?: number; version?: string | null; }; /** * Encodes characters for Cloudinary URL * Encodes some not allowed in Cloudinary parameter values twice: * hash (#), comma (,), slash (/), question mark (?), backslash (\) * * @see https://support.cloudinary.com/hc/en-us/articles/202521512-How-to-add-a-slash-character-or-any-other-special-characters-in-text-overlays- */ function cleanText(text: string): string { return encodeURIComponent(text).replace(/%(23|2C|2F|3F|5C)/g, '%25$1'); } /** * Generates a social sharing image with custom text using Cloudinary’s APIs. * * @see https://cloudinary.com/documentation/image_transformations#adding_text_captions */ export function generateSocialImage({ title, tagline, cloudName, imagePublicID, cloudinaryUrlBase = 'https://res.cloudinary.com', titleFont = 'arial', titleExtraConfig = '', taglineExtraConfig = '', taglineFont = 'arial', imageWidth = 1280, imageHeight = 669, textAreaWidth = 760, textLeftOffset = 480, titleGravity = 'south_west', taglineGravity = 'north_west', titleLeftOffset = null, taglineLeftOffset = null, titleBottomOffset = 254, taglineTopOffset = 445, textColor = '000000', titleColor, taglineColor, titleFontSize = 64, taglineFontSize = 48, version = null, }: GetShareImageConfig): string { // configure social media image dimensions, quality, and format const imageConfig = [ `w_${imageWidth}`, `h_${imageHeight}`, 'c_fill', 'q_auto', 'f_auto', ].join(','); // configure the title text const titleConfig = [ `w_${textAreaWidth}`, 'c_fit', `co_rgb:${titleColor || textColor}`, `g_${titleGravity}`, `x_${titleLeftOffset || textLeftOffset}`, `y_${titleBottomOffset}`, `l_text:${titleFont}_${titleFontSize}${titleExtraConfig}:${cleanText( title, )}`, ].join(','); // configure the tagline text const taglineConfig = tagline ? [ `w_${textAreaWidth}`, 'c_fit', `co_rgb:${taglineColor || textColor}`, `g_${taglineGravity}`, `x_${taglineLeftOffset || textLeftOffset}`, `y_${taglineTopOffset}`, `l_text:${taglineFont}_${taglineFontSize}${taglineExtraConfig}:${cleanText( tagline, )}`, ].join(',') : undefined; // combine all the pieces required to generate a Cloudinary URL const urlParts = [ cloudinaryUrlBase, cloudName, 'image', 'upload', imageConfig, titleConfig, taglineConfig, version, imagePublicID, ]; // remove any falsy sections of the URL (e.g. an undefined version) const validParts = urlParts.filter(Boolean); // join all the parts into a valid URL to the generated image return validParts.join('/'); } /** * @deprecated It's recommended to use named imports instead (`import { generateSocialImage } from '@jlengstorf/get-share-image'`). The default export will be removed in a future major version. */ export default generateSocialImage;