Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(functions): add social-media-square image type #4219

Merged
merged 4 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion functions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,14 @@ All of the below options can be given as query parameters, e.g. `?imType=og&noca
<td><code>imType</code></td>
<td>
<code>twitter</code> or <code>og</code> (short for
<a href="https://ogp.me">Open Graph</a>)
<a href="https://ogp.me">Open Graph</a>) or <code>social-media-square</code>
</td>
<td>
If present, will use fitting defaults for the generated image size:
<ul>
<li><code>twitter</code>: 800x418</li>
<li><code>og</code>: 1200x628</li>
<li><code>social-media-square</code>: 2160x2160, customizable using <code>imSquareSize=[number]</code></li>
</ul>
All below options will be ignored if <code>imType</code> is set to one of these values.
</td>
Expand Down
20 changes: 15 additions & 5 deletions functions/_common/grapherRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand All @@ -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: {
Expand All @@ -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
Expand All @@ -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)
),
Expand Down
2 changes: 2 additions & 0 deletions functions/_common/grapherTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
34 changes: 31 additions & 3 deletions functions/_common/imageOptions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import {
GrapherProgrammaticInterface,
GRAPHER_SQUARE_SIZE,
} from "@ourworldindata/grapher"
import {
DEFAULT_ASPECT_RATIO,
MIN_ASPECT_RATIO,
Expand All @@ -7,14 +11,16 @@ import {
DEFAULT_WIDTH,
DEFAULT_HEIGHT,
} from "./grapherRenderer.js"
import { GrapherStaticFormat } from "@ourworldindata/types"

export interface ImageOptions {
pngWidth: number
pngHeight: number
svgWidth: number
svgHeight: number
details: boolean
fontSize: number
fontSize: number | undefined
grapherProps?: Partial<GrapherProgrammaticInterface>
}
export const TWITTER_OPTIONS: ImageOptions = {
// Twitter cards are 1.91:1 in aspect ratio, and 800x418 is the recommended size
Expand All @@ -34,12 +40,34 @@ const OPEN_GRAPH_OPTIONS: ImageOptions = {
details: false,
fontSize: 21,
}
export const extractOptions = (params: URLSearchParams): ImageOptions => {
const options: Partial<ImageOptions> = {}
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<ImageOptions> = {}

// 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.
Expand Down
8 changes: 7 additions & 1 deletion packages/@ourworldindata/grapher/src/core/Grapher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
2 changes: 2 additions & 0 deletions packages/@ourworldindata/grapher/src/core/GrapherConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions packages/@ourworldindata/grapher/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down