diff --git a/functions/README.md b/functions/README.md index f5402687d66..bf039c79701 100644 --- a/functions/README.md +++ b/functions/README.md @@ -232,13 +232,14 @@ All of the below options can be given as query parameters, e.g. `?imType=og&noca imType twitter or og (short for - Open Graph) + Open Graph) or social-media-square If present, will use fitting defaults for the generated image size: All below options will be ignored if imType is set to one of these values. diff --git a/functions/_common/grapherRenderer.ts b/functions/_common/grapherRenderer.ts index 784c32607e5..7dd979a085a 100644 --- a/functions/_common/grapherRenderer.ts +++ b/functions/_common/grapherRenderer.ts @@ -82,7 +82,8 @@ async function fetchAndRenderGrapherToSvg( const svg = grapher.generateStaticSvg() grapherLogger.log("generateStaticSvg") - return svg + + return { svg, backgroundColor: grapher.backgroundColor } } export const fetchAndRenderGrapher = async ( @@ -94,12 +95,17 @@ export const fetchAndRenderGrapher = async ( const options = extractOptions(searchParams) console.log("Rendering", id.id, outType, options) - const svg = await fetchAndRenderGrapherToSvg(id, options, searchParams, env) + const { svg, backgroundColor } = await fetchAndRenderGrapherToSvg( + id, + options, + searchParams, + env + ) console.log("fetched svg") switch (outType) { case "png": - return png(await renderSvgToPng(svg, options)) + return png(await renderSvgToPng(svg, options, backgroundColor)) case "svg": return new Response(svg, { headers: { @@ -111,7 +117,11 @@ export const fetchAndRenderGrapher = async ( let initialized = false -export async function renderSvgToPng(svg: string, options: ImageOptions) { +export async function renderSvgToPng( + svg: string, + options: ImageOptions, + backgroundColor: string +) { if (!initialized) { await initializeSvg2Png(svg2png_wasm) initialized = true @@ -123,7 +133,7 @@ export async function renderSvgToPng(svg: string, options: ImageOptions) { // if we include details, pngHeight is only the height of the chart, but we also have an "appendix" at the bottom that we want to include height: options.details ? undefined : options.pngHeight, - backgroundColor: "#fff", + backgroundColor, fonts: [LatoRegular, LatoMedium, LatoBold, PlayfairSemiBold].map( (f) => new Uint8Array(f) ), diff --git a/functions/_common/grapherTools.ts b/functions/_common/grapherTools.ts index 990391ce476..51bedac65db 100644 --- a/functions/_common/grapherTools.ts +++ b/functions/_common/grapherTools.ts @@ -144,7 +144,9 @@ export async function initGrapher( bounds, staticBounds: bounds, baseFontSize: options.fontSize, + ...options.grapherProps, }) + grapher.isExportingToSvgOrPng = true grapher.shouldIncludeDetailsInStaticExport = options.details return grapher diff --git a/functions/_common/imageOptions.ts b/functions/_common/imageOptions.ts index 4fab7759338..0ccf0b3be43 100644 --- a/functions/_common/imageOptions.ts +++ b/functions/_common/imageOptions.ts @@ -1,3 +1,7 @@ +import { + GrapherProgrammaticInterface, + GRAPHER_SQUARE_SIZE, +} from "@ourworldindata/grapher" import { DEFAULT_ASPECT_RATIO, MIN_ASPECT_RATIO, @@ -7,6 +11,7 @@ import { DEFAULT_WIDTH, DEFAULT_HEIGHT, } from "./grapherRenderer.js" +import { GrapherStaticFormat } from "@ourworldindata/types" export interface ImageOptions { pngWidth: number @@ -14,7 +19,8 @@ export interface ImageOptions { svgWidth: number svgHeight: number details: boolean - fontSize: number + fontSize: number | undefined + grapherProps?: Partial } export const TWITTER_OPTIONS: ImageOptions = { // Twitter cards are 1.91:1 in aspect ratio, and 800x418 is the recommended size @@ -34,12 +40,34 @@ const OPEN_GRAPH_OPTIONS: ImageOptions = { details: false, fontSize: 21, } -export const extractOptions = (params: URLSearchParams): ImageOptions => { - const options: Partial = {} +const SOCIAL_MEDIA_SQUARE_OPTIONS: ImageOptions = { + pngWidth: 4 * GRAPHER_SQUARE_SIZE, + pngHeight: 4 * GRAPHER_SQUARE_SIZE, + svgWidth: GRAPHER_SQUARE_SIZE, + svgHeight: GRAPHER_SQUARE_SIZE, + details: false, + fontSize: undefined, + grapherProps: { + isSocialMediaExport: true, + staticFormat: GrapherStaticFormat.square, + }, +} +export const extractOptions = (params: URLSearchParams): ImageOptions => { // We have two special images types specified via the `imType` query param: if (params.get("imType") === "twitter") return TWITTER_OPTIONS else if (params.get("imType") === "og") return OPEN_GRAPH_OPTIONS + else if (params.get("imType") === "social-media-square") { + const squareOptions = SOCIAL_MEDIA_SQUARE_OPTIONS + if (params.has("imSquareSize")) { + const size = parseInt(params.get("imSquareSize")!) + squareOptions.pngWidth = size + squareOptions.pngHeight = size + } + return squareOptions + } + + const options: Partial = {} // Otherwise, query params can specify the size to be rendered at; and in addition we're doing a // bunch of normalization to make sure the image is rendered at a reasonable size and aspect ratio. diff --git a/packages/@ourworldindata/grapher/src/core/Grapher.tsx b/packages/@ourworldindata/grapher/src/core/Grapher.tsx index 0243e038ba2..99080b5f556 100644 --- a/packages/@ourworldindata/grapher/src/core/Grapher.tsx +++ b/packages/@ourworldindata/grapher/src/core/Grapher.tsx @@ -139,6 +139,7 @@ import { GRAPHER_FRAME_PADDING_VERTICAL, latestGrapherConfigSchema, validChartTypeCombinations, + GRAPHER_SQUARE_SIZE, } from "../core/GrapherConstants" import { loadVariableDataAndMetadata } from "./loadVariable" import Cookies from "js-cookie" @@ -2072,7 +2073,12 @@ export class Grapher case GrapherStaticFormat.landscape: return this.defaultBounds case GrapherStaticFormat.square: - return new Bounds(0, 0, 540, 540) + return new Bounds( + 0, + 0, + GRAPHER_SQUARE_SIZE, + GRAPHER_SQUARE_SIZE + ) default: return this.defaultBounds } diff --git a/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts b/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts index fd8bc6d38a5..1485a2658c4 100644 --- a/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts +++ b/packages/@ourworldindata/grapher/src/core/GrapherConstants.ts @@ -19,6 +19,8 @@ export const GRAPHER_LOADED_EVENT_NAME = "grapherLoaded" export const DEFAULT_GRAPHER_WIDTH = 850 export const DEFAULT_GRAPHER_HEIGHT = 600 +export const GRAPHER_SQUARE_SIZE = 540 + export const GRAPHER_FRAME_PADDING_VERTICAL = 16 export const GRAPHER_FRAME_PADDING_HORIZONTAL = 16 diff --git a/packages/@ourworldindata/grapher/src/index.ts b/packages/@ourworldindata/grapher/src/index.ts index 81efb82e166..b2476752247 100644 --- a/packages/@ourworldindata/grapher/src/index.ts +++ b/packages/@ourworldindata/grapher/src/index.ts @@ -14,6 +14,7 @@ export { GRAPHER_IS_IN_IFRAME_CLASS, DEFAULT_GRAPHER_WIDTH, DEFAULT_GRAPHER_HEIGHT, + GRAPHER_SQUARE_SIZE, STATIC_EXPORT_DETAIL_SPACING, DEFAULT_GRAPHER_ENTITY_TYPE, GRAPHER_LOADED_EVENT_NAME,