[
  {
    "path": ".gitignore",
    "content": "node_modules\nindex.js\nindex.d.ts\nindex.mjs\nindex.cjs\nindex.cjs.*\nindex.umd.*\nindex.esm.*\nindex.js.map\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"endOfLine\": \"lf\",\n  \"semi\": true,\n  \"singleQuote\": true,\n  \"tabWidth\": 2,\n  \"trailingComma\": \"all\"\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2020 Jason Lengstorf\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Generate Social Media Images Using Cloudinary\n\nThis 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).\n\n> **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)\n\n**This was created as part of an article series:**\n\n- [How to design a social sharing card template](https://www.learnwithjason.dev/blog/design-social-sharing-card)\n- [How the code in this package works](https://www.learnwithjason.dev/blog/auto-generate-social-image)\n\n## Installation\n\n```bash\n# install using npm\nnpm install --save @jlengstorf/get-share-image\n\n# install using yarn\nyarn add @jlengstorf/get-share-image\n```\n\nSee 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).\n\n## Example Usage\n\n```js\nimport getShareImage from '@jlengstorf/get-share-image';\n\nconst socialImage = getShareImage({\n  title: 'Deploy a Node.js App to DigitalOcean with SSL',\n  tagline: '#devops #nodejs #ssl',\n  cloudName: 'jlengstorf',\n  imagePublicID: 'lwj/blog-post-card',\n  titleFont: 'futura',\n  taglineFont: 'futura',\n  textColor: '232129',\n});\n```\n\nThis generates an image URL:\n\n```text\nhttps://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\n```\n\nWhich looks like this:\n\n![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)\n\n## Options\n\nThis utility function accepts a config object. Available options are as follows:\n\n| name               | required | description                                                          |\n| ------------------ | -------- | -------------------------------------------------------------------- |\n| title              | true     | (string) title text to be placed on the card                         |\n| tagline            |          | (string) tagline text to be placed on the card                       |\n| cloudName          | true     | (string) your Cloudinary cloud name (i.e. your username)             |\n| imagePublicID      | true     | (string) the public ID of your social image template                 |\n| cloudinaryUrlBase  |          | (string, default `https://res.cloudinary.com`) Cloudinary asset URL  |\n| titleFont          |          | (string, default `arial`) font to use for rendering title            |\n| titleExtraConfig   |          | (string) optional additional text overlay config                     |\n| taglineExtraConfig |          | (string) optional additional text overlay config                     |\n| taglineFont        |          | (string, default `arial`) font to use for rendering tagline          |\n| imageWidth         |          | (number, default `1280`) SEO image width (defaults to Twitter ratio) |\n| imageHeight        |          | (number, default `669`) SEO image height (defaults to Twitter ratio) |\n| textAreaWidth      |          | (number, default `760`) width of title and tagline text areas        |\n| textLeftOffset     |          | (number, default `480`) distance from left edge to start text boxes  |\n| titleGravity       |          | (string, default `south_west`) location the title is anchored from   |\n| taglineGravity     |          | (string, default `north_west`) location the tagline is anchored from |\n| titleLeftOffset    |          | (number, `null`) distance from left edge to start text boxes  |\n| taglineLeftOffset  |          | (number, default `null`) distance from left edge to start text boxes |\n| titleBottomOffset  |          | (number, default `254`) distance from bottom to start title text     |\n| taglineTopOffset   |          | (number, default `445`) distance from top to start tagline text      |\n| textColor          |          | (string, default `000000`) hex value for text color                  |\n| titleColor         |          | (string) hex value specific for title color. If this is not set, the color will be the one set to `textColor`   |\n| taglineColor       |          | (string) hex value specific for tagline color. If this is not set, the color will be the one set to `textColor` |\n| titleFontSize      |          | (number, default `64`) font size to use for the title                |\n| taglineFontSize    |          | (number, default `48`) font size to use for the tagline              |\n| version            |          | (string) optional version string for caching                         |\n\n### Setting config options\n\n```js\nconst socialImage = getShareImage({\n  title: 'My Post Title',\n  tagline: 'A tagline for the post',\n  cloudName: 'myusername',\n  imagePublicID: 'my-template-image.jpg',\n  titleExtraConfig: '_bold', // optional - set title font weight to bold\n  textColor: '663399', // optional — set the color to purple\n});\n```\n\n## Who is using this?\n\n- [Echobind](https://echobind.com/) with their [blog image generator](https://github.com/echobind/blog-image-generator)\n- [@jsjoeio](https://github.com/jsjoeio) on his [personal website](https://github.com/jsjoeio/joeprevite.com)\n- [Horacio Herrera](https://horacioh.com)\n- [@chris_berry](https://twitter.com/chris_berry) on his [blog](https://chrisberry.io)\n- [@codebender828](https://twitter.com/codebender828) on his [blog](https://jbakebwa.dev)\n- [@ryan_c_harris](https://twitter.com/ryan_c_harris) on his [blog](https://ryanharris.dev)\n- [Idiomatic Programmers](https://idiomaticprogrammers.com/)\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"@jlengstorf/get-share-image\",\n  \"type\": \"module\",\n  \"version\": \"1.0.1\",\n  \"source\": \"src/index.ts\",\n  \"exports\": {\n    \"import\": \"./dist/index.js\",\n    \"require\": \"./dist/index.cjs\",\n    \"types\": \"./index.d.ts\"\n  },\n  \"main\": \"./dist/index.cjs\",\n  \"module\": \"./dist/index.esm.js\",\n  \"unpkg\": \"./dist/index.umd.js\",\n  \"author\": \"Jason Lengstorf <jason@lengstorf.com> (https://lengstorf.com)\",\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/jlengstorf/get-share-image.git\"\n  },\n  \"scripts\": {\n    \"build\": \"microbundle\",\n    \"dev\": \"microbundle watch\",\n    \"prepublish\": \"npm run build\"\n  },\n  \"files\": [\n    \"./dist\"\n  ],\n  \"types\": \"dist/index.d.ts\",\n  \"devDependencies\": {\n    \"microbundle\": \"^0.15.1\",\n    \"typescript\": \"^5.3.3\"\n  }\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "type Gravity =\n  | 'north_east'\n  | 'north'\n  | 'north_west'\n  | 'west'\n  | 'south_west'\n  | 'south'\n  | 'south_east'\n  | 'east'\n  | 'center';\n\ntype GetShareImageConfig = {\n  title: string;\n  tagline?: string;\n  cloudName: string;\n  imagePublicID: string;\n\n  /**\n   * Only needed if you have a custom Cloudinary URL\n   * @default 'https://res.cloudinary.com'\n   */\n  cloudinaryUrlBase?: string;\n\n  /**\n   * @default 'arial'\n   */\n  titleFont?: string;\n  titleExtraConfig?: string;\n  taglineExtraConfig?: string;\n\n  /**\n   * @default 'arial'\n   */\n  taglineFont?: string;\n\n  /**\n   * @default 1280\n   */\n  imageWidth?: number;\n\n  /**\n   * @default 669\n   */\n  imageHeight?: number;\n\n  /**\n   * @default 760\n   */\n  textAreaWidth?: number;\n\n  /**\n   * @default 480\n   */\n  textLeftOffset?: number;\n\n  /**\n   * @default 'south_west'\n   */\n  titleGravity?:\n    | 'north_east'\n    | 'north'\n    | 'north_west'\n    | 'west'\n    | 'south_west'\n    | 'south'\n    | 'south_east'\n    | 'east'\n    | 'center';\n\n  /**\n   * @default 'north_west'\n   */\n  taglineGravity?:\n    | 'north_east'\n    | 'north'\n    | 'north_west'\n    | 'west'\n    | 'south_west'\n    | 'south'\n    | 'south_east'\n    | 'east'\n    | 'center';\n  titleLeftOffset?: number | null;\n  taglineLeftOffset?: number | null;\n\n  /**\n   * @default 254\n   */\n  titleBottomOffset?: number;\n\n  /**\n   * @default 445\n   */\n  taglineTopOffset?: number;\n\n  /**\n   * @default '000000'\n   */\n  textColor?: string;\n  titleColor: string;\n  taglineColor: string;\n\n  /**\n   * @default 64\n   */\n  titleFontSize?: number;\n\n  /**\n   * @default 48\n   */\n  taglineFontSize?: number;\n\n  version?: string | null;\n};\n\n/**\n * Encodes characters for Cloudinary URL\n * Encodes some not allowed in Cloudinary parameter values twice:\n *   hash (#), comma (,), slash (/), question mark (?), backslash (\\)\n *\n * @see https://support.cloudinary.com/hc/en-us/articles/202521512-How-to-add-a-slash-character-or-any-other-special-characters-in-text-overlays-\n */\nfunction cleanText(text: string): string {\n  return encodeURIComponent(text).replace(/%(23|2C|2F|3F|5C)/g, '%25$1');\n}\n\n/**\n * Generates a social sharing image with custom text using Cloudinary’s APIs.\n *\n * @see https://cloudinary.com/documentation/image_transformations#adding_text_captions\n */\nexport function generateSocialImage({\n  title,\n  tagline,\n  cloudName,\n  imagePublicID,\n  cloudinaryUrlBase = 'https://res.cloudinary.com',\n  titleFont = 'arial',\n  titleExtraConfig = '',\n  taglineExtraConfig = '',\n  taglineFont = 'arial',\n  imageWidth = 1280,\n  imageHeight = 669,\n  textAreaWidth = 760,\n  textLeftOffset = 480,\n  titleGravity = 'south_west',\n  taglineGravity = 'north_west',\n  titleLeftOffset = null,\n  taglineLeftOffset = null,\n  titleBottomOffset = 254,\n  taglineTopOffset = 445,\n  textColor = '000000',\n  titleColor,\n  taglineColor,\n  titleFontSize = 64,\n  taglineFontSize = 48,\n  version = null,\n}: GetShareImageConfig): string {\n  // configure social media image dimensions, quality, and format\n  const imageConfig = [\n    `w_${imageWidth}`,\n    `h_${imageHeight}`,\n    'c_fill',\n    'q_auto',\n    'f_auto',\n  ].join(',');\n\n  // configure the title text\n  const titleConfig = [\n    `w_${textAreaWidth}`,\n    'c_fit',\n    `co_rgb:${titleColor || textColor}`,\n    `g_${titleGravity}`,\n    `x_${titleLeftOffset || textLeftOffset}`,\n    `y_${titleBottomOffset}`,\n    `l_text:${titleFont}_${titleFontSize}${titleExtraConfig}:${cleanText(\n      title,\n    )}`,\n  ].join(',');\n\n  // configure the tagline text\n  const taglineConfig = tagline\n    ? [\n        `w_${textAreaWidth}`,\n        'c_fit',\n        `co_rgb:${taglineColor || textColor}`,\n        `g_${taglineGravity}`,\n        `x_${taglineLeftOffset || textLeftOffset}`,\n        `y_${taglineTopOffset}`,\n        `l_text:${taglineFont}_${taglineFontSize}${taglineExtraConfig}:${cleanText(\n          tagline,\n        )}`,\n      ].join(',')\n    : undefined;\n\n  // combine all the pieces required to generate a Cloudinary URL\n  const urlParts = [\n    cloudinaryUrlBase,\n    cloudName,\n    'image',\n    'upload',\n    imageConfig,\n    titleConfig,\n    taglineConfig,\n    version,\n    imagePublicID,\n  ];\n\n  // remove any falsy sections of the URL (e.g. an undefined version)\n  const validParts = urlParts.filter(Boolean);\n\n  // join all the parts into a valid URL to the generated image\n  return validParts.join('/');\n}\n\n/**\n * @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.\n */\nexport default generateSocialImage;\n"
  }
]