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
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>&</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>→</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>→</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>→</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>→</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