Skip to content

Keep declared open-graph image size for images Quarto cannot measure - #15040

Merged
cderv merged 7 commits into
mainfrom
fix/issue-12016
Oct 14, 2026
Merged

cderv merged 7 commits into
mainfrom
fix/issue-12016

Conversation

@cderv

@cderv cderv commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

With site-url set, og:image:width and og:image:height disappear for an open-graph image that Quarto cannot measure, even when image-width and image-height are declared. #12016 reported it for a JPEG, which #15050 now measures. The remaining cases are remote images and formats Quarto does not read, such as SVG, GIF or WebP.

Root cause

resolveImageMetadata in src/project/types/website/website-meta.ts always replaced the declared size with the measured one, which is undefined when the image can't be measured. Without site-url this code is skipped, which is why removing site-url hides the problem.

Fix

The measured size is still used when Quarto can read the file, so the tags describe the actual pixels. Otherwise the declared size is kept.

Keeping the declared size raises the question of which image it describes. A size declared in the site open-graph options describes the site image, so it no longer applies when the page supplies its own image: image, a page open-graph image, or a preview image found in the page content. That image only gets the dimensions declared in the page open-graph options, each one separately. image: false and the website.image fallback keep the site image and its size.

Compatibility

This changes the output of sites without site-url. Before, the site-declared size was emitted on every page, including pages with their own image. A site that declared one size because all its page images share it now loses these tags on pages that set an image.

The check is on whether the page declares an image, not on which file it is, so a page that repeats the site image also loses the site size.

To keep the tags, declare image-width and image-height under open-graph in the page front matter, or once in shared metadata such as _metadata.yml. Top-level image-width and image-height in the page front matter are not read for open-graph.

Fixes #12016

Checklist

I have (if applicable):

  • referenced the GitHub issue this PR closes
  • updated the appropriate changelog in the PR
  • ensured the present test suite passes
  • added new tests
  • created a separate documentation PR in Quarto's website repo and linked it to this PR
AI-assisted PR
  • AI tool used: Claude Code
  • Codebase grounding: local clone
  • Human review: I have reviewed, tested, and verified the AI-generated content before submitting.

Note: autonomous AI agents submitting PRs without human oversight are not permitted — see the Code of Conduct.

@cderv cderv added the websites Issues creating websites label Oct 13, 2026
@posit-snyk-bot

posit-snyk-bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
✅ Open Source Security 0 0 0 0 0 issues
✅ Licenses 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

cderv added a commit that referenced this pull request Oct 14, 2026
imageSize only measured files named ".png", so JPEG feed images,
listing items and og:image/twitter:image never got a height or
width. imageSize now detects PNG and JPEG from the file's header
bytes, not its extension, so uppercase ".PNG" and ".jpeg" names are
measured too.

- Read JPEG dimensions with a small marker scan in src/core/jpeg.ts,
  with no new dependency. Baseline, progressive and lossless JPEGs
  are all covered.
- Add isPng() in src/core/png.ts, checking the PNG spec signature.
- Other formats (SVG, GIF, WebP) still return no size. SVG often has
  no pixel size, and social platforms reject it as og:image.

Related to #12016 and #15040.
cderv added 4 commits October 14, 2026 12:49
With `site-url` set, resolveImageMetadata replaced the declared
`image-width`/`image-height` with the size read by imageSize, which only
reads PNG. A JPEG social image therefore lost the size the user wrote
(#12016), and removing `site-url` only hid it by skipping this path.

The measured size now wins when the file can be read, and the declared
size is the fallback. Letting the declared size always win would have
been simpler, but page metadata is merged with the site's open-graph
data: a post that sets its own `image` would inherit the size declared
for the site image and emit it for a different file. So a size declared
at site level is dropped when the page sets its own image without
declaring a size for it. This also removes the inherited size that
pages already got without `site-url`.

There is no override of a measured size: og:image:width/height describe
the image's pixel dimensions, which is what the file reports.

Fixes #12016
…red for

The previous change dropped the site's declared size when the page sets
its own image, but decided ownership too coarsely:

- A page declaring only one of image-width/image-height kept the other
  site dimension, producing a mixed size. Each dimension now comes from
  the page alone when the page owns the image.
- `image: false` counted as a page image, yet mergedSiteAndDocumentData
  keeps the site image in that case, so the site image lost its size.
  The check now uses the same truthy predicate as the merge.
- A preview image discovered in the page content (.preview-image or a
  named file) was decided after opengraphMetadata ran, so it inherited
  the site's declared size. Discovery now applies the same rule. The
  website.image fallback is still a site image and keeps the site size.
A page with an empty `open-graph:` entry gets null for that key, and
typeof null is "object". The object checks in opengraphMetadata and
usePageDeclaredImageSize let it through, and reading a property off the
null aborted the render on sites whose website.open-graph is an object.
Before this branch mergeConfigs ignored the null source, so the site
configuration was used without error.

Treat null as no page open-graph block, so the site configuration applies
as it did before.
cderv added 2 commits October 14, 2026 12:58
Since #15050, Quarto measures JPEG images for open-graph metadata, and a
measured size wins over a declared one. The site's mark.jpg is 34x52, so
the fixtures that use it as the site image now emit 34 and 52 instead of
the declared 1200 and 892. The declared-size cases that Quarto cannot
measure keep their expectations in post-svg-declared.qmd.
Since #15050 measures JPEG files, the fixtures using the local mark.jpg
site image no longer exercise the declared-size fallback at the site
level. A remote image is never measured, so with site-url its declared
image-width and image-height must still reach og:image:width and
og:image:height. This fails on main, where the size is replaced by the
undefined measurement.
cderv added a commit to quarto-dev/quarto-web that referenced this pull request Oct 14, 2026
The image row described only PNG measurement and suggested JPEG as a
case for declared sizes. Since quarto-dev/quarto-cli#15050, Quarto also
measures JPEG, so declared sizes only matter for remote images and
formats like WebP or GIF.

quarto-dev/quarto-cli#15040 also stops a page image from inheriting the
sizes declared in website: open-graph. The check is whether the page
supplies an image, not whether it differs from the site image, and only
the page's open-graph block is read, not top-level image-width/height.
Sites without site-url that used the site sizes as defaults lose those
tags, so document the migration through page or _metadata.yml
open-graph metadata, and note that directory sizes also apply to the
site image on pages without their own image.

All statements checked by rendering test sites with a 1.11 dev build
and with 1.10.19.
#15050 now measures JPEG images, so the original JPEG case from #12016
no longer depends on this change. What remains is keeping a declared size
for images Quarto cannot measure, such as remote ones, and limiting a
site-declared size to the site image. The second part changes output for
sites without site-url that declared a global size because all their page
images share it: a page that sets its own image, even the site image
again, no longer gets the site size. The entry names where to move the
declaration.
@cderv cderv changed the title Fix open-graph image size dropped for non-PNG images with site-url Keep declared open-graph image size for images Quarto cannot measure Oct 14, 2026
@cderv
cderv merged commit 5b25f5d into main Oct 14, 2026
51 checks passed
@cderv
cderv deleted the fix/issue-12016 branch October 14, 2026 12:05
cderv added a commit to quarto-dev/quarto-web that referenced this pull request Oct 14, 2026
* Document how open-graph image-width and image-height apply

The image rows of both social metadata tables said a declared
image-width/image-height improves "your Twitter Card". Twitter Card
metadata never reads a declared size; only Open Graph does.

With site-url set, Quarto measures PNG images and uses that size over a
declared one. From Quarto 1.11 (quarto-dev/quarto-cli#15040), a declared
size is kept for images Quarto can't measure (JPEG, SVG, ...), and a
page that brings its own image no longer inherits the size declared for
the site image. Explain both in the Open Graph table, and point the
Twitter Card row there instead.

* Explain open-graph image size behaviour in its own section

The image row described only PNG measurement and suggested JPEG as a
case for declared sizes. Since quarto-dev/quarto-cli#15050, Quarto also
measures JPEG, so declared sizes only matter for remote images and
formats like WebP or GIF.

quarto-dev/quarto-cli#15040 also stops a page image from inheriting the
sizes declared in website: open-graph. The check is whether the page
supplies an image, not whether it differs from the site image, and only
the page's open-graph block is read, not top-level image-width/height.
Sites without site-url that used the site sizes as defaults lose those
tags, so document the migration through page or _metadata.yml
open-graph metadata, and note that directory sizes also apply to the
site image on pages without their own image.

All statements checked by rendering test sites with a 1.11 dev build
and with 1.10.19.
cderv added a commit that referenced this pull request Oct 14, 2026
Relative image paths for og:image, twitter:image and listing feeds
resolved against the wrong directory on pages in subdirectories. The
URLs pointed to files that do not exist and lost width and height.
Each source now resolves against the file it is written in: site-level
images in _quarto.yml against the project, page and metadata images
against the document, and feed.image against the project.

- Site image, open-graph.image and twitter-card.image become
  project-relative before they reach the page metadata, so the URL,
  the size and the copied file all come from the project.
- The site and page metadata merge works on a copy, so a page image
  no longer leaks into the site object shared by all pages.
- A listing's own image is joined onto the listing's directory for the
  feed, using the helper listing item images already used
  (projectRelativeHref).
- The site URL join uses joinUrl, so a site-url ending in / no longer
  produces a doubled slash.

Paths starting with / and URLs are unchanged. Without site-url the
value is written as is.

The website.image fallback came from #5389 without a base decision.
Related to #15040, which edits the same block to keep a declared image
size (#12016).

Fixes #15039
cderv added a commit that referenced this pull request Oct 15, 2026
Remove the top-level `image-width` and `image-height` options, which
nothing read. Open Graph sizes come from `open-graph` or from
measuring the image. Because the schema typed them as strings, a
numeric value such as `image-width: 1200` failed the render, for an
option that did nothing. Wiring them up was rejected: the same names
already size the about-page image, so a top-level meaning would be
ambiguous.

- Remove both keys from the website document schema. Front matter
  accepts undeclared keys, so numeric and string values now render
  and are ignored.
- Point the `image` option description at `open-graph` for declaring
  the preview image size.

See #15040 and quarto-dev/quarto-web#2282 for the sizing rules and
the existing "aren't used" note.

Related to #12016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

websites Issues creating websites

Projects

None yet

Development

Successfully merging this pull request may close these issues.

image-width and image-height not working with open-graph in some cases

2 participants