Skip to main content

Use Cog to generate the images README

ID
85ae14c
date
2024-01-03 08:34:23+00:00
author
Alex Chan <alex@alexwlchan.net>
parent
ebdbe42
message
Use Cog to generate the images README

Closes #5
changed files
8 files, 180 additions, 295 deletions

Changed files

config.fish (5632) → config.fish (5782)

diff --git a/config.fish b/config.fish
index 7260d3c..14647f9 100644
--- a/config.fish
+++ b/config.fish
@@ -155,11 +155,14 @@ __create_python_script_alias flickr/fluser_lookup.py
 __create_python_script_alias fs/emptydir.py
 __create_python_script_alias git/find_big_commits.py
 __create_python_script_alias git/git-cloc.py
+__create_python_script_alias images/chunky_pixels.py
 __create_python_script_alias images/copycrop.py
 __create_python_script_alias images/kn_cover_image.py
 __create_python_script_alias images/images_only_pdf.py
-__create_python_script_alias images/srgbify.py
+__create_python_script_alias images/pdfthumb.py
 __create_python_script_alias images/reborder.py
+__create_python_script_alias images/save_xkcd.py
+__create_python_script_alias images/srgbify.py
 __create_python_script_alias images/tint_image.py
 __create_python_script_alias text/fix_twemoji.py
 __create_python_script_alias text/fix_twitter_thread.py

images/README.md (6107) → images/README.md (11197)

diff --git a/images/README.md b/images/README.md
index 017310c..c7b539e 100644
--- a/images/README.md
+++ b/images/README.md
@@ -4,10 +4,172 @@ These scripts are for working with images and other visual material.
 
 ## The individual scripts
 
+<!-- [[[cog
+
+# This adds the root of the repo to the PATH, which has cog_helpers.py
+from os.path import abspath, dirname
+import sys
+
+sys.path.append(abspath(dirname(dirname("."))))
+
+import cog_helpers
+
+folder_name = "images"
+
+scripts = [
+    {
+        "usage": "chunky_pixels.py [PATH] [PIXEL_SIZE]",
+        "description": """
+        redraw an image using “chunky” pixels – that is, replacing every NxN block of pixels with a single colour.
+        <p>
+          <table>
+            <tr>
+              <td>
+                <img src="examples/flowers.jpg">
+                original
+              </td>
+              <td>
+                <img src="examples/flowers_12.jpg">
+                N=12
+              </td>
+              <td>
+                <img src="examples/flowers_34.jpg">
+                N=34
+              </td>
+              <td>
+                <img src="examples/flowers_68.jpg">
+                N=68
+              </td>
+              <td>
+                <img src="examples/flowers_204.jpg">
+                N=204
+              </td>
+            </tr>
+          </table>
+        </p>
+        """,
+    },
+    {
+        "name": "copycrop.py",
+        "description": """
+        this script will “copy” the crop from one image pair to another.
+        <p>For example, suppose I have a full-screen screenshot and a crop to a small region of the screen:</p>
+        <p>
+          <table>
+            <tr>
+              <td><img src="examples/light_original.png"></td>
+              <td>&amp;</td>
+              <td><img src="examples/light_crop.png"></td>
+            </tr>
+          </table>
+        </p>
+        I can use this tool to extract the equivalent region from a second screenshot:
+        <p>
+          <table>
+            <tr>
+              <td><img src="examples/dark_original.png"></td>
+              <td>&rarr;</td>
+              <td><img src="examples/dark_crop.png"></td>
+            </tr>
+          </table>
+        </p>
+        I often use this when making images for my website, to create identical light mode and dark mode screenshots.
+        """
+    },
+    {
+        "usage": "images_only_pdf.py [PATH]",
+        "description": """
+        take a PDF, and create a new PDF which just has the images filling the page.
+        I use this to work around an odd behaviour of the “Scan Document” feature in Notes.app, where it adds a large white border around scanned images that I don’t want.
+        <p><strong>Note:</strong> this script overwrites the original file.</p>
+        """
+    },
+    {
+        "usage": "kn_cover_image.py [PATH]",
+        "description": """
+        prepare a cover image for an article on my website.
+        <p>
+          I use Keynote to compose a lot of my promo images, then I export the slide to an image.
+          The slide includes a white rectangle that marks the rough boundary of the image; this script extracts the selected region, adjusts the crop so it's an exact 2:1 ratio, and converts the colour profile to sRGB.
+        </p>
+        <p>
+          <table>
+            <tr>
+              <td><img src="examples/kn_example.jpeg"></td>
+              <td>&rarr;</td>
+              <td><img src="examples/kn_example.cropped.jpg"></td>
+            </tr>
+          </table>
+        </p>
+        """
+    },
+    {
+        "name": "move_cover_image",
+        "description": """
+        copy an image from my Desktop folder into my book tracker.
+        This is very specific to my setup and unlikely to be useful to anyone else.
+        """
+    },
+    {
+        "usage": "pdfthumb.py [PATH] --page=[PAGE]",
+        "description": """
+        get a PNG thumbnail of a specific page of a PDF, for example <code>pdfthumb pattern.pdf --page=3</code> will create a thumbnail of the third page.
+        """
+    },
+    {
+        "usage": "reborder.py [PATH] [BORDER_WIDTH]",
+        "description": """
+        replace empty space around an image with consistent white padding.
+        I use this when cleaning up screenshots, to get equal padding around the entire image.
+        <p>
+          <table>
+            <tr>
+              <td><img src="examples/reborder_original.png"></td>
+              <td>&rarr;</td>
+              <td><img src="examples/reborder_50.png"></td>
+            </tr>
+          </table>
+        </p>
+        """
+    },
+    {
+        "usage": "save_xkcd.py [COMIC_NUMBER]",
+        "description": """
+        saves a single comic from <a href="https://xkcd.com/">xkcd</a>, plus some metadata.
+        """
+    },
+    {
+        "usage": "srgbify.py [PATH]",
+        "description": """
+        convert an image in-place to have an sRGB colour profile.
+        """
+    },
+    {
+        "usage": "tint_image.py [PATH] [HEX_COLOUR]",
+        "description": """
+        take a greyscale image, and create a version which is tinted with the specified colour.
+        This works by creating an RGBA image which has the specified colour on every pixel, but controlling the intensity with the alpha value.
+        <p>
+          <table>
+            <tr>
+              <td><img src="examples/grayscale_circle.png"></td>
+              <td>&rarr;</td>
+              <td><img src="examples/grayscale_circle.ff0000.png"></td>
+            </tr>
+          </table>
+        </p>
+        I don’t use this script very often, but I checked it in because I thought it was a neat trick I didn’t want to forget.
+        """
+    },
+]
+
+cog_helpers.create_description_table(folder_name=folder_name, scripts=scripts)
+
+]]]-->
 <dl>
   <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/chunky_pixels">
-      <code>chunky_pixels [PATH] [PIXEL_SIZE]</code>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/images/chunky_pixels.py">
+      <code>chunky_pixels.py [PATH] [PIXEL_SIZE]</code>
     </a>
   </dt>
   <dd>
@@ -40,10 +202,9 @@ These scripts are for working with images and other visual material.
     </p>
   </dd>
 
-
   <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/copycrop">
-      <code>copycrop</code>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/images/copycrop.py">
+      <code>copycrop.py</code>
     </a>
   </dt>
   <dd>
@@ -72,8 +233,8 @@ These scripts are for working with images and other visual material.
   </dd>
 
   <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/images_only_pdf">
-      <code>images_only_pdf [PATH]</code>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/images/images_only_pdf.py">
+      <code>images_only_pdf.py [PATH]</code>
     </a>
   </dt>
   <dd>
@@ -103,7 +264,6 @@ These scripts are for working with images and other visual material.
       </table>
     </p>
   </dd>
-  
 
   <dt>
     <a href="https://github.com/alexwlchan/scripts/blob/main/images/move_cover_image">
@@ -116,8 +276,8 @@ These scripts are for working with images and other visual material.
   </dd>
 
   <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/pdfthumb">
-      <code>pdfthumb</code>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/images/pdfthumb.py">
+      <code>pdfthumb.py [PATH] --page=[PAGE]</code>
     </a>
   </dt>
   <dd>
@@ -144,26 +304,8 @@ These scripts are for working with images and other visual material.
   </dd>
 
   <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/retrobatch">
-      <code>retrobatch [...]</code>
-    </a>
-  </dt>
-  <dd>
-    an alias for running the Retrobatch image processor <a href="https://flyingmeat.com/retrobatch/docs-1.0/commandline/">from the command-line</a>
-  </dd>
-
-  <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/save_flickr">
-      <code>save_flickr [FLICKR_URL]</code>
-    </a>
-  </dt>
-  <dd>
-    saves a single image from Flickr, plus some metadata.
-  </dd>
-
-  <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/save_xkcd">
-      <code>save_xkcd [COMIC_NUMBER]</code>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/images/save_xkcd.py">
+      <code>save_xkcd.py [COMIC_NUMBER]</code>
     </a>
   </dt>
   <dd>
@@ -180,8 +322,8 @@ These scripts are for working with images and other visual material.
   </dd>
 
   <dt>
-    <a href="https://github.com/alexwlchan/scripts/blob/main/images/tint_image">
-      <code>tint_image [PATH] [HEX_COLOUR]</code>
+    <a href="https://github.com/alexwlchan/scripts/blob/main/images/tint_image.py">
+      <code>tint_image.py [PATH] [HEX_COLOUR]</code>
     </a>
   </dt>
   <dd>
@@ -199,3 +341,4 @@ These scripts are for working with images and other visual material.
     I don’t use this script very often, but I checked it in because I thought it was a neat trick I didn’t want to forget.
   </dd>
 </dl>
+<!-- [[[end]]] (checksum: 50c5b16562f339dec3e3f32fbb6e21a5) -->

images/chunky_pixels (2126) → images/chunky_pixels.py (2126)

diff --git a/images/chunky_pixels b/images/chunky_pixels.py
similarity index 100%
rename from images/chunky_pixels
rename to images/chunky_pixels.py

images/overwrite_with_srgb.retrobatch (3882) → images/overwrite_with_srgb.retrobatch (0)

diff --git a/images/overwrite_with_srgb.retrobatch b/images/overwrite_with_srgb.retrobatch
deleted file mode 100644
index d8bb96f..0000000
--- a/images/overwrite_with_srgb.retrobatch
+++ /dev/null
@@ -1,113 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>previewBackgroundColorState</key>
-	<integer>0</integer>
-	<key>processes</key>
-	<array>
-		<dict>
-			<key>canvasOrigin</key>
-			<string>{560, 160}</string>
-			<key>class</key>
-			<string>OTWriteNode</string>
-			<key>inputFileNameTokens</key>
-			<data>
-			BAtzdHJlYW10eXBlZIHoA4QBQISEhAdOU0FycmF5AISECE5TT2Jq
-			ZWN0AIWEAWkBkoSEhBFPVFRva2VuRmllbGRUb2tlbgCUkoSEhAhO
-			U1N0cmluZwGUhAErCiRGaWxlTmFtZSSGhoY=
-			</data>
-			<key>inputLossyCompressionQuality</key>
-			<real>0.5</real>
-			<key>inputOpenFolderOnFinish</key>
-			<false/>
-			<key>inputOverwriteExistingFiles</key>
-			<integer>1</integer>
-			<key>inputProgressive</key>
-			<false/>
-			<key>inputRemoveAlphaChannelIfOpaque</key>
-			<true/>
-			<key>inputReplaceOriginalImage</key>
-			<integer>1</integer>
-			<key>inputTIFFCompressionAlgorithm</key>
-			<integer>5</integer>
-			<key>outputFolderURLBookmarkData</key>
-			<data>
-			Ym9vazAEAAAAAAQQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
-			AAAAAAAAAAAAIAMAAAQAAAADAwAAAAIAAAUAAAABAQAAVXNlcnMA
-			AAAKAAAAAQEAAGFsZXh3bGNoYW4AAAUAAAABAQAAcmVwb3MAAAAO
-			AAAAAQEAAGFsZXh3bGNoYW4ubmV0AAADAAAAAQEAAHNyYwAHAAAA
-			AQEAAF9pbWFnZXMABAAAAAEBAAAyMDIzHAAAAAEGAAAQAAAAIAAA
-			ADQAAABEAAAAXAAAAGgAAAB4AAAACAAAAAQDAAAVXQAAAAAAAAgA
-			AAAEAwAAQeMDAAAAAAAIAAAABAMAADfuCgAAAAAACAAAAAQDAACV
-			jhYAAAAAAAgAAAAEAwAAUaYWAAAAAAAIAAAABAMAAE2nFgAAAAAA
-			CAAAAAQDAADPlDMAAAAAABwAAAABBgAAqAAAALgAAADIAAAA2AAA
-			AOgAAAD4AAAACAEAAAgAAAAABAAAQcS6Xb1mzIwYAAAAAQIAAAIA
-			AAAAAAAADwAAAAAAAAAAAAAAAAAAAAgAAAAEAwAABQAAAAAAAAAE
-			AAAAAwMAAPUBAAAIAAAAAQkAAGZpbGU6Ly8vDAAAAAEBAABNYWNp
-			bnRvc2ggSEQIAAAABAMAAAAAhxE5AAAACAAAAAAEAABBxEAH4AAA
-			ACQAAAABAQAARkJENjhFN0MtRjYyNy00MDVCLTgzREUtOEZCNzBF
-			M0MyQzU1GAAAAAECAACBAAAAAQAAAO8TAAABAAAAAAAAAAAAAAAB
-			AAAAAQEAAC8AAAAAAAAAAQUAAOkAAAABAgAAZDBhZjA1YWUxNmY1
-			MWViZDI3Yjk3ZTY0N2MzOGFhYTIxMWZkYjNkN2M0M2Y5OGQ2NTEz
-			MjNhYWMxNmQ2YzljZjswMDswMDAwMDAwMDswMDAwMDAwMDswMDAw
-			MDAwMDswMDAwMDAwMDAwMDAwMDIwO2NvbS5hcHBsZS5hcHAtc2Fu
-			ZGJveC5yZWFkLXdyaXRlOzAxOzAxMDAwMDEyOzAwMDAwMDAwMDAz
-			Mzk0Y2Y7MDE7L3VzZXJzL2FsZXh3bGNoYW4vcmVwb3MvYWxleHds
-			Y2hhbi5uZXQvc3JjL19pbWFnZXMvMjAyMwAAAADYAAAA/v///wEA
-			AAAAAAAAEQAAAAQQAACEAAAAAAAAAAUQAAAYAQAAAAAAABAQAABM
-			AQAAAAAAAEAQAAA8AQAAAAAAAAIgAAAYAgAAAAAAAAUgAACIAQAA
-			AAAAABAgAACYAQAAAAAAABEgAADMAQAAAAAAABIgAACsAQAAAAAA
-			ABMgAAC8AQAAAAAAACAgAAD4AQAAAAAAADAgAAAkAgAAAAAAAAHA
-			AABsAQAAAAAAABHAAAAgAAAAAAAAABLAAAB8AQAAAAAAABDQAAAE
-			AAAAAAAAAIDwAAAsAgAAAAAAAA==
-			</data>
-			<key>outputOriginalFolderPath</key>
-			<string>/Users/alexwlchan/repos/alexwlchan.net/src/_images/2023</string>
-			<key>outputUTI</key>
-			<string>original</string>
-			<key>outputsIds</key>
-			<array/>
-			<key>proccessId</key>
-			<string>8e9b92f0-c105-41bd-bfa0-1f5717fe871d</string>
-		</dict>
-		<dict>
-			<key>canvasOrigin</key>
-			<string>{320, 160}</string>
-			<key>class</key>
-			<string>OTColorProfileNode</string>
-			<key>inputMatchToProfile</key>
-			<integer>1</integer>
-			<key>inputProfileURLBookmark</key>
-			<data>
-			</data>
-			<key>inputRenderingIntent</key>
-			<integer>0</integer>
-			<key>inputStripColorProfile</key>
-			<integer>0</integer>
-			<key>outputsIds</key>
-			<array>
-				<string>8e9b92f0-c105-41bd-bfa0-1f5717fe871d</string>
-			</array>
-			<key>proccessId</key>
-			<string>8d5aa9bc-0e45-4780-afb3-328d0b3e237a</string>
-		</dict>
-		<dict>
-			<key>canvasOrigin</key>
-			<string>{80, 160}</string>
-			<key>class</key>
-			<string>OTFilesNode</string>
-			<key>outputsIds</key>
-			<array>
-				<string>8d5aa9bc-0e45-4780-afb3-328d0b3e237a</string>
-			</array>
-			<key>proccessId</key>
-			<string>1f92c379-3eb8-450d-a3df-a90d5eb1f024</string>
-			<key>urls</key>
-			<array/>
-		</dict>
-	</array>
-	<key>version</key>
-	<integer>0</integer>
-</dict>
-</plist>

images/pdfthumb (1884) → images/pdfthumb.py (1846)

diff --git a/images/pdfthumb b/images/pdfthumb.py
similarity index 98%
rename from images/pdfthumb
rename to images/pdfthumb.py
index 202dbce..a6df244 100755
--- a/images/pdfthumb
+++ b/images/pdfthumb.py
@@ -19,7 +19,7 @@ import sys
 import subprocess
 import tempfile
 
-from PyPDF2 import PdfReader, PdfWriter  # pip3 install --user PyPDF2==3.0.1
+from pypdf import PdfReader, PdfWriter
 
 
 def parse_args(argv):

images/retrobatch (230) → images/retrobatch (0)

diff --git a/images/retrobatch b/images/retrobatch
deleted file mode 100755
index b4c661b..0000000
--- a/images/retrobatch
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/usr/bin/env bash
-# Alias for running Retrobatch from the command-line.
-#
-# See https://flyingmeat.com/retrobatch/docs-1.0/commandline/
-
-set -o errexit
-set -o nounset
-
-/Applications/Retrobatch.app/Contents/MacOS/Retrobatch "$@"

images/save_flickr (3364) → images/save_flickr (0)

diff --git a/images/save_flickr b/images/save_flickr
deleted file mode 100755
index 20d9b71..0000000
--- a/images/save_flickr
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/env python3
-"""
-Downloads and saves a single Flickr image, plus a bit of metadata.
-
-I'm not using this to create a complete archive of Flickr but to create
-a mini-library of my personal favourites.
-"""
-
-import datetime as dt
-import json
-import pathlib
-import shutil
-import sys
-import tempfile
-
-import bs4
-import hyperlink
-import urllib3
-import xmltodict  # pip3 install xmltodict==0.12.0
-
-
-BACKUP_ROOT = pathlib.Path("/Volumes/Media (Sapphire)") / "backups" / "flickr"
-
-
-def get_canonical_url(url):
-    http = urllib3.PoolManager()
-
-    seen_urls = set(url)
-
-    url = url.split("/in/")[0]
-
-    while True:
-        resp = http.request(
-            "GET", url,
-            redirect=False,
-            headers={"User-Agent": "urllib3"}
-        )
-
-        try:
-            url = resp.headers["Location"]
-        except KeyError:
-            return url
-
-        if url in seen_urls:
-            raise ValueError("Circular redirect: {url}")
-        else:
-            seen_urls.add(url)
-
-
-def build_url(base, params):
-    u = hyperlink.URL.from_text(base)
-    for k, v in params.items():
-        u = u.set(k, v)
-    return u
-
-
-def get_oembed_data(canonical_url):
-    http = urllib3.PoolManager()
-
-    request_url = build_url(
-        "https://www.flickr.com/services/oembed/",
-        params={"url": url}
-    )
-
-    oembed_resp = http.request("GET", str(request_url))
-    assert oembed_resp.status == 200, oembed_resp.status
-    return xmltodict.parse(oembed_resp.data)["oembed"]
-
-
-def get_description(url):
-    http = urllib3.PoolManager()
-
-    resp = http.request("GET", url)
-    assert resp.status == 200, resp.status
-
-    soup = bs4.BeautifulSoup(resp.data, "html.parser")
-    return soup.find("meta", attrs={"name": "description"}).attrs["content"]
-
-
-def save_image(out_dir, oembed_data):
-    http = urllib3.PoolManager()
-
-    img_url = oembed_data["url"]
-    filename = hyperlink.URL.from_text(img_url).path[-1]
-
-    # See https://stackoverflow.com/q/17285464/1558022
-    resp = http.request("GET", img_url, preload_content=False)
-
-    with (out_dir / filename).open("wb") as out:
-        while True:
-            data = resp.read(1024)
-            if not data:
-                break
-            out.write(data)
-
-    resp.release_conn()
-
-
-if __name__ == "__main__":
-    try:
-        url = sys.argv[1]
-    except IndexError:
-        sys.exit(f"Usage: {__file__} <FLICKR_URL>")
-
-    canonical_url = get_canonical_url(url)
-    oembed_data = get_oembed_data(canonical_url)
-
-    parsed_url = hyperlink.URL.from_text(canonical_url.strip("/"))
-
-    assert parsed_url.path[0] == "photos", parsed_url
-
-    flickr_id = parsed_url.path[-1]
-    assert flickr_id.isnumeric()
-
-    author_name = oembed_data["author_name"]
-
-    author_url = oembed_data["author_url"].strip("/")
-    creator = hyperlink.URL.from_text(author_url).path[-1]
-
-    backup_dir = BACKUP_ROOT / creator
-    backup_dir.mkdir(exist_ok=True)
-
-    description = get_description(canonical_url)
-
-    flickr_info = {
-        "url": url,
-        "canonical_url": canonical_url,
-        "saved_at": dt.datetime.now().isoformat(),
-        "description": description,
-        "oembed_data": oembed_data
-    }
-
-    json_string = json.dumps(flickr_info, indent=2, sort_keys=True)
-
-    (backup_dir / f"{flickr_id}.json").write_text(json_string)
-
-    save_image(backup_dir, oembed_data)
-
-    print(backup_dir)

images/save_xkcd (1117) → images/save_xkcd.py (1117)

diff --git a/images/save_xkcd b/images/save_xkcd.py
similarity index 100%
rename from images/save_xkcd
rename to images/save_xkcd.py