From ab8df23807cc4c11768ef40196bcade70ebec23a Mon Sep 17 00:00:00 2001 From: David Steele Date: Sat, 1 Aug 2026 19:14:39 +0700 Subject: [PATCH] Show a card rather than the logo where a link is posted. The image a link to the documentation showed where it was posted was the project logo at 96 by 96. The sites that show a preview lay one out in a frame of 1.91 to 1 and crop what does not fit, so a square image lost about a quarter of its height off the top and another off the bottom, and the logo was drawn edge to edge with nothing to give the crop. It was also well under the 1200 by 627 those sites ask for, so what survived the crop had been scaled up from too little to begin with. The card is laid out at 1200 by 630, which is the shape the frame already is, and carries the header lockup -- the mark, the name and the tagline -- at 960 wide and centered. That is the middle 80 percent of the card, which also leaves the whole of it inside the 2 to 1 frame X crops to. The background is opaque because a site composites what is transparent onto a background of its own choosing, and dark artwork on a background nobody has said anything about is a poor bet. The variable that names the image is project-card and the one that turns it off is card, since a 1200 by 630 preview image is not a logo. logo.png went with them, the preview being the only thing it was for, and it is the entry that changes in the linter skip list. logo.svg stays and is unaffected: it is the favicon, and it is the bare mark rather than the lockup. A page now says how big the card is, what it shows, and that a preview of it is a card rather than a thumbnail beside the text. The size is read out of the file rather than written down beside it, so the two cannot disagree. The html-logo variable is removed. It was never set, and the div it added was hidden by the style from the point the header began drawing the mark and the name itself, so it had been dead since the site was modernized. --- doc/lib/command/render/html.py | 44 +++++++++++++----- doc/manifest.xml | 7 ++- doc/release.py | 2 +- doc/resource/card.png | Bin 0 -> 48591 bytes doc/resource/html/default.css | 5 -- doc/resource/logo.png | Bin 1736 -> 0 bytes test/lib/command/lint/lint.py | 2 +- test/src/module/doc/command/doc_test.py | 11 +++-- .../module/doc/command/render/html_test.py | 43 +++++++++++------ .../src/module/test/command/lint/lint_test.py | 2 +- 10 files changed, 75 insertions(+), 41 deletions(-) create mode 100644 doc/resource/card.png delete mode 100644 doc/resource/logo.png diff --git a/doc/lib/command/render/html.py b/doc/lib/command/render/html.py index b011b91a2..c8c2f8605 100644 --- a/doc/lib/command/render/html.py +++ b/doc/lib/command/render/html.py @@ -10,6 +10,7 @@ change to the tool changed nothing about the documentation, and output that is a import os import re import shutil +import struct from common.error import ToolError from common.log import * @@ -74,11 +75,12 @@ class HtmlElement: class HtmlBuilder: """Builds a page from the elements it holds.""" - def __init__(self, name, title, favicon, logo, description): + def __init__(self, name, title, favicon, card, card_size, description): self.name = name self.title = title self.favicon = favicon - self.logo = logo + self.card = card + self.card_size = card_size self.description = description self.body = HtmlElement("body") @@ -149,9 +151,17 @@ class HtmlBuilder: if self.favicon is not None: result += '\n' % self.favicon - if self.logo is not None: + # The card is what a link to the page looks like where it is posted. It is laid out at 1200x630 because that is the frame + # the sites that show a preview crop to, and a card that is not that shape loses its top and bottom to the crop. + if self.card is not None: + result += '\n' % self.card result += '\n' - result += '\n' % self.logo + result += '\n' % self.card_size[0] + result += '\n' % self.card_size[1] + result += '\n' % self._escape(self.name) + + # Without this the preview is a thumbnail beside the text rather than the card above it + result += '\n' if self.description is not None: result += '\n' % self._escape(self.description) @@ -193,19 +203,18 @@ class DocHtmlPage(DocExecute): tagline = var_store.get("project-tagline") + card = None if var_store.test("card", "n") else var_store.get("project-card") + builder = HtmlBuilder( var_store.replace_str("{[project]}" + ("" if tagline is None else " - " + tagline)), var_store.replace_str(title + ("" if subtitle is None else " - " + subtitle)), var_store.get("project-favicon"), - None if var_store.test("logo", "n") else var_store.get("project-logo"), + card, + None if card is None else _png_size(os.path.join(self.manifest.path_doc, "resource", card)), var_store.replace_str(xml_node_field(self.root, "description", True).strip()), ) header = builder.body.add_new("div", "page-header") - - if var_store.get("html-logo") is not None: - header.add_new("div", "page-header-logo", content="{[html-logo]}") - header.add_new("div", "page-header-title", content=title) if subtitle is not None: @@ -534,6 +543,17 @@ class DocHtmlPage(DocExecute): return element +#################################################################################################################################### +def _png_size(file): + """Read what a png says it measures. + + The card says its size in the page so a crawler can lay the preview out before it has the image, and the size is read from the + image rather than written down so the two cannot disagree.""" + + with open(file, "rb") as handle: + return struct.unpack(">II", handle.read(24)[16:24]) + + #################################################################################################################################### def _resource_render(text, line_comment=False): """A style or a script with what it says about itself taken out. @@ -602,10 +622,10 @@ def html_render(manifest, path_doc, path_out, exe): if favicon is not None: shutil.copyfile(os.path.join(path_doc, "resource", favicon), os.path.join(path_out, favicon)) - logo = var_store.get("project-logo") + card = var_store.get("project-card") - if logo is not None and not var_store.test("logo", "n"): - shutil.copyfile(os.path.join(path_doc, "resource", logo), os.path.join(path_out, logo)) + if card is not None and not var_store.test("card", "n"): + shutil.copyfile(os.path.join(path_doc, "resource", card), os.path.join(path_out, card)) if not var_store.test("sponsor", "n"): path_sponsor = os.path.join(path_doc, "resource/sponsor") diff --git a/doc/manifest.xml b/doc/manifest.xml index 63deda5d3..a4c2a695d 100644 --- a/doc/manifest.xml +++ b/doc/manifest.xml @@ -8,11 +8,11 @@ / PostgreSQL - - logo.png + card.png logo.svg - - y + + y n @@ -27,7 +27,6 @@ Copyright &copy; 2015-{[release-year]}, The PostgreSQL Global Development Group, <a href="{[github-url-license]}">MIT License</a>. Updated {[release-date]} - diff --git a/doc/release.py b/doc/release.py index 46f307901..323444dff 100755 --- a/doc/release.py +++ b/doc/release.py @@ -309,7 +309,7 @@ def cmd_dist(config, dev): "--var=project-url-root=index.html", "--exclude=metric", "--exclude=news", - "--var=logo=n", + "--var=card=n", "--var=news=n", "--var=sponsor=n", ] diff --git a/doc/resource/card.png b/doc/resource/card.png new file mode 100644 index 0000000000000000000000000000000000000000..b136006a0dc9c67b3b334008cbff9682827218ef GIT binary patch literal 48591 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 OX%wGx{@5l@a`vCX`!S6G diff --git a/test/lib/command/lint/lint.py b/test/lib/command/lint/lint.py index f6e20d8b1..7a82cfc19 100644 --- a/test/lib/command/lint/lint.py +++ b/test/lib/command/lint/lint.py @@ -17,7 +17,7 @@ from common.storage import path_list_recurse # Files that are exempt from the content checks. A binary file is an unscannable place to hide content, so each entry must be a # deliberate, reviewable decision. _FILE_SKIP_LIST = ( - "doc/resource/logo.png", # Project logo + "doc/resource/card.png", # Card shown where a link to the documentation is posted "test/data/filecopy.table.bin", # Binary test fixture "doc/resource/git-history.cache", # Generated from git history, contains non-ASCII author names ) diff --git a/test/src/module/doc/command/doc_test.py b/test/src/module/doc/command/doc_test.py index 7c2bd1a1e..2adeec946 100644 --- a/test/src/module/doc/command/doc_test.py +++ b/test/src/module/doc/command/doc_test.py @@ -5,6 +5,7 @@ variables the caller gave before the documents that use them.""" #################################################################################################################################### import os +import struct import tempfile from harness.test import * @@ -122,7 +123,7 @@ MANIFEST = """ pgBackRest pgbackrest / - logo.png + card.png logo.svg Updated {[release-date]} release @@ -174,7 +175,11 @@ def _repo(path): file_write(os.path.join(path, "doc/resource/html/default.css"), "body { color: black; }\n") file_write(os.path.join(path, "doc/resource/html/default.js"), "// script\n") file_write(os.path.join(path, "doc/resource/slogo.svg"), "\n") - file_write(os.path.join(path, "doc/resource/logo.png"), "png") + # Only the header of the card is needed, since that is all that is read of it + file_write( + os.path.join(path, "doc/resource/card.png"), + b"\x89PNG\r\n\x1a\n" + struct.pack(">I", 13) + b"IHDR" + struct.pack(">II", 1200, 630), + ) file_write(os.path.join(path, "doc/resource/logo.svg"), "svg") file_write(os.path.join(path, "doc/resource/sponsor/one.png"), "png") @@ -306,7 +311,7 @@ def test_doc(): assert_equal(sorted(path_list(path_out)), ["html", "man", "markdown"]) assert_equal( sorted(path_list(os.path.join(path_out, "html"))), - ["default.css", "default.js", "index.html", "logo.png", "logo.svg", "slogo.svg", "sponsor", "user-guide.html"], + ["card.png", "default.css", "default.js", "index.html", "logo.svg", "slogo.svg", "sponsor", "user-guide.html"], ) # What goes beside the pages is copied rather than rendered diff --git a/test/src/module/doc/command/render/html_test.py b/test/src/module/doc/command/render/html_test.py index 128cc32a4..c13cc40f6 100644 --- a/test/src/module/doc/command/render/html_test.py +++ b/test/src/module/doc/command/render/html_test.py @@ -5,6 +5,7 @@ linefeed goes is what makes one build of the documentation comparable with the n #################################################################################################################################### import os +import struct import tempfile from harness.test import * @@ -222,7 +223,7 @@ def _page(path, key="user-guide", config=None): def test_element(): """A page is built as a tree of elements so the code that decides what it holds never deals with markup.""" - builder = HtmlBuilder("Name", "Title", None, None, None) + builder = HtmlBuilder("Name", "Title", None, None, None, None) body = builder.body body.add_new("div", "one", content="Content") @@ -236,13 +237,20 @@ def test_element(): def test_builder(): """A page says what it is.""" - builder = HtmlBuilder("pgBackRest", "Guide & More", "logo.svg", "logo.png", "How to use it.") + builder = HtmlBuilder("pgBackRest", "Guide & More", "logo.svg", "card.png", (1200, 630), "How to use it.") result = builder.render(analytics=True) assert_in("', result) - assert_in('', result) + + # The card says where it is and what shape it is, so a preview can be laid out before the image has been fetched + assert_in('', result) + assert_in('', result) + assert_in('', result) + assert_in('', result) + assert_in('', result) + assert_in('', result) assert_in('', result) assert_in('', result) @@ -404,12 +412,17 @@ def test_html_render(): with tempfile.TemporaryDirectory() as path: _doc_path(path) - file_write(os.path.join(path, "resource/logo.png"), "png") + # Only the header of the card is needed, since that is all that is read of it, and a size of its own shows the page says + # what the file measures rather than what the tool expects it to + file_write( + os.path.join(path, "resource/card.png"), + b"\x89PNG\r\n\x1a\n" + struct.pack(">I", 13) + b"IHDR" + struct.pack(">II", 400, 200), + ) file_write(os.path.join(path, "resource/logo.svg"), "svg") file_write(os.path.join(path, "resource/sponsor/one.png"), "png") manifest = _manifest(path) - manifest.var_store.add("project-logo", "logo.png") + manifest.var_store.add("project-card", "card.png") manifest.var_store.add("project-favicon", "logo.svg") path_out = os.path.join(path, "output/html") @@ -419,10 +432,15 @@ def test_html_render(): assert_equal( sorted(path_list(path_out)), - ["default.css", "default.js", "index.html", "logo.png", "logo.svg", "slogo.svg", "sponsor", "user-guide.html"], + ["card.png", "default.css", "default.js", "index.html", "logo.svg", "slogo.svg", "sponsor", "user-guide.html"], ) assert_equal(path_list(os.path.join(path_out, "sponsor")), ["one.png"]) + index = file_read(os.path.join(path_out, "index.html")) + + assert_in('', index) + assert_in('', index) + # The style and the script carry no comments, since the page carries them for a browser rather than for a reader, but # where their lines fall is left alone so one build can still be compared with the next assert_equal(file_read(os.path.join(path_out, "default.css")), "body\n{\n color: black;\n}\n") @@ -544,14 +562,10 @@ def test_page_other(): with tempfile.TemporaryDirectory() as path: manifest = _manifest(_doc_path(path, user_guide=OTHER_GUIDE)) - manifest.var_store.add("html-logo", "") render = manifest.render_get(RENDER_HTML) page = DocHtmlPage(manifest, "user-guide", render.menu, False).process() - # A logo of its own for the header - assert_in('