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 000000000..b136006a0 Binary files /dev/null and b/doc/resource/card.png differ diff --git a/doc/resource/html/default.css b/doc/resource/html/default.css index 22d2e220f..b37f3a80a 100644 --- a/doc/resource/html/default.css +++ b/doc/resource/html/default.css @@ -221,11 +221,6 @@ Header font-size: 1.25rem; } -.page-header-logo -{ - display: none; -} - /*********************************************************************************************************************************** Menu ***********************************************************************************************************************************/ diff --git a/doc/resource/logo.png b/doc/resource/logo.png deleted file mode 100644 index 3aa85867d..000000000 Binary files a/doc/resource/logo.png and /dev/null differ 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('