![]() |
thumbspage — Turn Folders into HTML Image Galleries |
---|
Version: | 2.0, October 5, 2020 (changes) |
License: | Provided freely, but with no warranties of any kind |
Author: | © M. Lutz (learning-python.com) 2016-2020 |
Install: | Unzip thumbspage's download, install the Pillow library |
Run with: | Python 3.X, on any platform supporting this and Pillow |
See also: | web page, live demos, screenshots, examples, code |
This is the thumbspage content-creators guide. It includes an overview of the program, detailed usage information, and a look at what's been upgraded in each release. Whether you consider yourself a developer or an end user, you'll find resources here to help get you started building image galleries with thumbspage.
Because thumbspage adds items to the images source folder and may rotate photos there, you are encouraged to read this guide first—especially its usage caution—before running thumbspage on valued picture collections. But if you're in a hurry: for a quick preview, try a live demo, packaged examples, or the code. You can also click the image above for screenshots, and take a brief tour of this program at its web page.
This section introduces the basics of thumbspage's roles and operation. Read this first if you're looking for a quick summary.
In short, this program allows you to view or display a folder of images in a web browser, without having to run a web server, and in a format that's both simple and noticeably better than browser defaults.
thumbspage turns an images folder into an easily viewed gallery. It automatically makes image thumbnails, an HTML thumbnail-links index page, and HTML image-viewer pages for all the images in a source folder, all of which can be customized by content creators. The mostly static results can be viewed offline or online in any web browser.
In a bit more detail, given a folder of image files, this program generates an HTML index page with generated thumbnail links for each image in the folder. This page's links in turn open either the full-size images directly using browser-native (i.e., built-in) display; or generated HTML viewer pages, with the image dynamically scaled to your view, gallery navigation and slideshow links, and file-info popups on filename clicks.
Apart from image scaling and interactive tools, the net effect is intentionally static: generated results reflect the folder's contents at build time only, but do not require a web server, and can be viewed both offline and online in any desktop or mobile browser. As such, this program can be used both for websites, and non-web use cases such as program documentation and general viewing.
When run (using the techniques explored ahead), this script skips non-image files; uses optional header and footer HTML index inserts; makes an optional bullet list for subfolders in the images folder; and creates the output index page in the images folder itself, along with a subfolder for thumbnail images and viewer pages' HTML. The resulting gallery is complete and self-contained, and ready to be viewed or published.
After running this program, you can view or publish its results in a variety of ways:
index.html
by default).
_thumbspage
and index.html
by default, respectively).
As a client example, the website that hosts thumbspage uses it for 42 galleries today, some of which are viewable both online at the site, and offline in program download packages. For a sampling, see this site's demos.
thumbspage began as a builder of simple index pages that used generated thumbnail images for their links, but its feature set has evolved over time in response to usage experience. Among its new highlights added in recent releases:
You can read the full story on new releases ahead. Move on to the next section to start learning how to use thumbspage to view and display your photos.
This section covers the basics of viewing the galleries which thumbspage creates. It's oriented towards gallery users, though creators might find its functionality overview helpful too. If you're looking for details on running the program to make galleries, though, see the builders' section.
By today's standards, thumbspage galleries are straightforward and intuitive to use. They work and may be viewed on any desktop or mobile browser, and both online and offline. No extensions must be installed in browsers to view thumbspage galleries, though JavaScript should be enabled for the best user-interface (UI) experience, and some browsers have known quirks that can impact gallery behavior (there's more on JavaScript and browsers in Viewing Tips ahead).
Once built, a thumbspage gallery consists of an index page opened initially, and one viewer page per image opened from either thumbnails on the index page, or direct page-address URLs. The next sections describe how these two page types are used.
Index pages are the usual entry point into a gallery. Here's one captured on desktop and mobile, and its live version. They display a table of thumbnail images, which scroll both horizontally and vertically as needed, and normally open larger image-viewer pages when tapped (or clicked).
By default, index pages also display the image folder's basename, and a bullet-list of links that open subfolders if any are present. Beginning with 2.0, thumbspage index pages also usually display a floating Top button near page bottom after a scroll, much like that in this guide. Click (or tap) Top to jump to the top of the index page immediately—to read its preface or open its first image, for example. Top appears only after you scroll down far enough, and won't show up on smaller indexes.
While many index pages follow the preceding norm, individual galleries can also customize it with tailored header and footer content above and below the thumbnails table. They might also use thumbnails that open raw displays instead of viewer pages, and may opt out of either floating Top buttons or subfolder bullet lists. Experiment with galleries you visit to see which options they support.
Index-page thumbnail clicks by default open viewer pages (a.k.a. image-viewer pages in this doc) to display a single image. Here's one captured on desktop and mobile, and its live version. Viewer pages display the image's filename; the image itself, automatically scaled to fill the window or display's available space (and grow or shrink in both directions with desktop-window resizes and mobile device rotations); and a toolbar of four or five named action items at the bottom. The toolbar scrolls horizontally to reveal buttons, but this is usually needed only on small displays with large fonts.
Everything on a viewer page does something when tapped, as the following sections will describe.
All told, there are six or seven actions available to users on viewer pages, two of which are invoked with display widgets:
control
or command
with +
/-
),
and may yield a larger image depending on browser and display size.
Use the browser's Back operation (e.g., button, gesture, or swipe) to return to the
viewer page. The Full toolbar button also zooms the image in most contexts, but
we have to move ahead to see how.
The remaining viewer-page actions are all requested by tapping named toolbar buttons, one of which is optional and may be omitted when a gallery is built:
Slideshows apply to all viewer pages visited in a single browser tab. They stop (or pause) when you leave a gallery's pages, and may be cancelled or restarted at any time by pressing Auto again in any viewer page. The Prev and Next toolbar buttons work during, and do not cancel, an active slideshow; use these to skip or move to images during the show. You can also use Index or your browser's Back function to pick an image from the gallery's index page at which to continue the slideshow in progress. In most contexts, a Back that reenters a gallery resumes a slideshow left in progress too; press Auto to cancel whenever you like (and see ahead for a rare exception on Safari).
Because of these limitations, the Full button and feature may be omitted on a per-gallery basis (it's enabled in thumbspage demos to show how it works). When Full is available, it provides a per-image blowup/quick-zoom option (e.g., before and after), that may be easier than raw-image zooming via image taps. Users looking for a true fullscreen experience, however, may be better served by using browsers which support a manual fullscreen that spans multiple pages, including those in thumbspage slideshows; see the next section's fullscreen note for pointers. Note that Full may be hidden on small displays; scroll the toolbar left as needed.
As usual with UIs, first-hand experience is also a great way to learn how to use thumbspage galleries; browse the examples in the thumbspage package, and test-drive the live demos at thumbspage's host site for more on index- and viewer-page interaction.
This section collects additional assorted tips for viewing thumbspage galleries. Content you'll find here:
Because viewer pages suffer most when JavaScript is off, they give you an alert when yours is disabled; here's one on desktop and mobile. JavaScript can be misused, but is fully harmless in code that thumbspage generates itself, and is essential for the sort of interactive-UI functionality offered by thumbspage galleries. In fact, most of today's web isn't quite usable without it—for better or worse. Please turn JavaScript on for the best thumbspage experience.
Caveats: while no browsers or devices are known to be unable to display thumbspage galleries, some browsers have relevant quirks mentioned in other notes ahead; usability is naturally subject to browser and platform changes; and viewability is prone to decline with screen size (all bets are off on your watch). That said, HTML may not be a portability utopia, but thumbspage manages to make it work.
F11
key (with fn
if
needed) puts the browser in fullscreen mode, which persists for all
pages visited until explicitly disabled. This works in Chrome, Firefox,
Edge, Internet Explorer, and likely others, and allows thumbspage Auto
slideshows to run completely in fullscreen mode.
control
+command
+f
.
A shift
+command
+f
may additionally hide the toolbar in Chrome (but not Firefox, currently),
and you can also go fullscreen by clicking the upper-left-corner
green button or using menu fullscreen icons.
For additional tips and other platforms and browsers, try a web search. Manual and persistent fullscreen isn't available everywhere, but it generally beats thumbspage's one-page Full where possible. You may still find thumbspage's Full useful as a quick zoom, though, especially on browsers that lack a manual option today. With any luck, those browser will make Full fully superfluous in the not-too-distant tomorrow.
file://
URLs offer more options. On mobile devices, however, offline viewing isn't
always as seamless, but you'll have to move on to the next two notes for
the Android and iOS offline stories. Safari/Mac users: see also the
caveat ahead for the rare use case of
browser Back returns to offline galleries.
content://
URIs.
While these may be spun as security measures, that argument has been tiredly
used for many an Android
breakage.
thumbspage galleries are known to work offline on Androids Nougat, Oreo, Pie, and 10, but its releases and devices vary too widely to give universal advice here. If your offline gallery fails, try the following work-arounds that have proved successful in some contexts:
file://url
if present)
file:///sdcard/...
)
https://...
)
If you're technically inclined, you might also try running a
web server app
on your device and viewing your gallery on localhost
or a
dedicated port number; this is wholly untested (but arguably awesome).
Caveat: the upcoming Android 11 will clamp permissions down further
(read the latest edicts
here),
but its impact on browser apps remains to be seen.
You can read more about this Android Chrome issue with a web search like
this.
The thumbspage testing team has recently also found a settings-related
issue in 2020's Android Firefox—it leaves an odd blank strip at the bottom
of viewer pages if you change its default settings to move the browser navigation
toolbar to the top of the screen; and has reported an outright bug in version 53
of Android Opera—though it does not impact thumbspage galleries (and is
fixed in version 60), this browser strangely truncates the remainder of a page at a
/*
character sequence in text formatted as code with <pre>
.
At the end of the day, thumbspage is completely dependent on browsers which change frequently and arbitrarily, and have an unfortunate history of fragility (indeed, at times it seems a website could be laid low by a good sneeze). For best results, clear settings that cause issues on Android, use similarly obscure browser behavioral switches with care, and try other browsers as a last resort when browser bugs—and enhancements—break your viewing experience.
For more tips on using thumbspage galleries, viewers are also encouraged to browse the rest of this document; although it's focused on gallery builds from here on, you can also find usage details along the way.
This section is meant for gallery creators, who run thumbspage to build new galleries. It describes thumbspage install requirements, inputs and results, customization options, and other operational details. It's also a comprehensive tutorial that doesn't assume you are already a command-line wizard—which means more advanced readers may want to skim parts useful to others. If you're looking for info on how to use galleries, try the viewers' section first.
If you enjoy learning by example, you can also study the thumbspage demo sites online here and here for sparse but fast tutorials. These sites list their run logs, and use custom headers and settings which you don't need to code if the defaults work for your galleries.
This section covers the basic logistics of using the thumbspage program to build galleries. The pages of the galleries it builds work on all browsers and platforms and require no extra installs or infrastructure, as covered earlier.
thumbspage itself is a Python program that runs on all major platforms, and is provided in source-code form which you run with your local Python. To install the program, download its zipfile from the Download section of the following web page and unzip it on your computer:
learning-python.com/thumbspage.htmlThe thumbspage program also requires installs of Python 3.X (any X) to run its source code, plus the third-party Pillow (a.k.a. PIL) image library to extend the installed Python 3.X with image-processing tools. If they're not already present, fetch and install both these items from the following sites, respectively (or search the web for other links):
www.python.org/downloads/ pypi.python.org/pypi/Pillow
thumbspage can be run to build galleries on any platform that runs Python 3.X and Pillow, and has the required folder and file access permissions. For example, this program has been verified to run on Windows, Mac OS, Linux, and Android. The latter has unique requirements and permission rules; see the notebox below for usage on Android, as well as the similarly constrained iOS. For a related Pillow install pointer, see also this page.
As discussed ahead, as of version 1.7 thumbspage also uses the pure-Python and third-party piexif library to update image Exif tags, but this library's code is included and shipped with thumbspage itself on all platforms, and does not require a separate install.
A note for programmers: Pillow is used from Python for thumbnail generation, image rotation, and Exif-tag processing—which all occur at gallery-build (i.e., program-run) time. Images are scaled at image-display time instead by JavaScript code in generated viewer pages—which mix HTML, CSS, and JavaScript in a tangled and browser-specific morass that is the norm for web development today. The stew manages to work well anyhow.
thumbspage on mobile:
thumbspage galleries can be both viewed and built on mobile devices.
This guide often focuses on the mobile portability of generated pages, but it's also
possible to run the program itself on your Android smartphone or tablet from
a Python-aware app.
For instance, thumbspage galleries can be built in the
Termux app, after running both
of the following commands (more details
here,
though the -y
suggested there isn't recommended—it skips asking if you wish to
proceed):
pkg install python ndk-sysroot clang make libjpeg-turbo
pip install Pillow
After this, thumbspage commands work in Termux the same as elsewhere.
You can also launch thumbspage command lines on Android in
Pydroid 3,
after running the same pip
command in its Terminal or using its Pip GUI, and
other Python apps may provide additional options. To sample the flavor of thumbspage
builds in Termux, check out the Android demos
here,
here, and
here, plus the console log
here.
Caveats: keyboards naturally boost command-line usability on cramped smartphones.
Android also imposes proprietary access rules which limit the folders
available to your Python app—and hence thumbspage;
for more on its access rules which are beyond this guide's scope, see
this doc.
thumbspage may run on iOS too (e.g., the
Pythonista app bundles a
version of Pillow), but this is untested, and iOS's access rules have
historically been
tighter
than Android's.
Once you've installed thumbspage and its required tools per the preceding section, you're ready to start turning your image folders into galleries. This section demonstrates how to run thumbspage on your computer. It also goes over command-line basics for users new to the technique.
For a basic launch of thumbspage, run script
thumbspage.py
from a command line with no command-line arguments.
It can be run from a console (e.g., Terminal on Mac OS
and Linux, Command Prompt on Windows, and whatever qualifies as
a command shell in Android apps); and after opening its file in most Python IDEs
(e.g., PyEdit and
IDLE).
thumbspage's main options are selected with five console replies, or their enter-key defaults,
on each run. The following example session gives user inputs in bold font. In its
prompts, inputs are described in []
, and defaults are given in
()
(on Windows, be sure to use py -3
at the start of
your command line, and \
instead of /
in your pathnames):
/.../content$ python3 /MY-STUFF/Code/thumbspage/thumbspage.py Images folder path [. or dir] (enter=.)? trnpix Clean thumbs folder [y or n] (enter=y)? y Thumbs per row [int] (enter=4)? Thumb max size [x, y] (enter=(100, 100))? Use image-viewer pages [y or n] (enter=y)? y Running Cleaning: trnpix/_thumbspage/1996-first-pybook.png Cleaning: trnpix/_thumbspage/1996-first-pybook.png.html ... Skipping: .DS_Store Making thumbnail: trnpix/_thumbspage/1996-first-pybook.png Making thumbnail: trnpix/_thumbspage/1998-puertorico-1.jpg ... Skipping: _cut Skipping: _HOW.txt ... Generating thumbnails index page Generating view page for: 1996-first-pybook.png Generating view page for: 1998-puertorico-1.jpg ... Finished: see the results in the images folder.
This section documents the input replies in the preceding usage example, numbering
them from the first ?
prompt to the last. As we'll see in the
next section, the first of these can now be provided with a command-line argument
instead; this bumps the others up in the list, but the options work the same either way.
Reply #1 is where you specify the source-image folder, which is also
where results will appear. This reply accepts an absolute or relative folder
pathname. For example, entering folder
means that folder in the directory
where the script is being run, and /folder/folder
and
C:\folder\folder
denote absolute paths to your image folder on Unix and Windows, respectively.
A solitary .
means the directory where the script is being run, and is the
default.
You should generally clean the thumbs folder (reply #2) unless images have only been added, and use viewer pages unless they don't work well in your use case (reply #5). There's more on both of these options in the building tips ahead.
Replies #3 and #4 allow you to tailor index-page thumbnails on each run:
If you change your mind or make a mistake:
you can cancel a run by typing a break or EOF key sequence (e.g.,
control
+ c
or d
) at any prompt,
and thumbspage reports input errors nicely as demonstrated in this
console log.
Besides the basic console interaction of the prior section, thumbspage also
allows you to pass in the folder name as a sole command-line argument instead
of a prompt reply. This allows you to use shell auto-completion on long folder
names, though it's probably more useful in a console window than an IDE.
Folder argument or not, you can also automate a launch by using command-line
<
syntax to provide canned parameter inputs one per line in
a piped-in text file. These options yield at least three ways to start the
program:
$ python3 thumbspage.py Images folder path [. or dir] (enter=.)? trnpix ...4 other parameters prompted from input... $ python3 thumbspage.py trnpix ...4 other parameters prompted from input... $ python3 thumbspage.py < inputs.txt
In the last of these modes, parameters in the piped-in file inputs.txt
would like look this (use an empty line to accept a prompt's default, and omit the
first line if you provide the folder name in the command line instead):
trnpix y 4 y
Naturally, you can combine all these with shell syntax for pipes and output
redirection (|
and > file
).
For example, the following Unix session both sends parameters to the
script from one file, and saves its output to another (it also
assumes shell variable $C
has been set to name the folder where
you unzip programs like thumbspage, and opts for 128-pixel thumbnails, 3 per row):
$ cat params.txt y 3 128, 128 y $ python3 $C/thumbspage/thumbspage.py trnpix < params.txt > report.txt $ more report.txt ...view output here...
For more background on such shell techniques, try the off-site overviews
here,
here, and
here.
For more thumbspage usage examples, browse its
examples
folder.
The console input prompts and arguments above are really just the first level of
options in thumbspage. The sections Customization
and Building Tips that follow
cover additional configuration options and usage details.
First, the next section explores more basics for users new to running
programs from command lines.
Update: the optional folder-name command-line argument was added in version 1.7. To better reflect this, 1.7 also moved folder name to be the first prompt when not provided on the command line; this invalidates some older session logs where folder name was asked later, but the difference is trivial. 2.0 added nicer input-error reporting, replacing exception tracebacks.
Because thumbspage is run from a command line, you need to know the basics of folder pathnames in the console realm. Luckily, this is much simpler than it may sound; as noted, the pathname you input at reply #1 can be either:
.
for the current folder, ..
for one level
up, or the simple name of a local item in the folder you're working in,
like photos
)
/Users/you/photos
on Unix, C:\Users\you\photos
on Windows, or
/sdcard/photos
on Android)
For instance, when running thumbspage via command lines,
you can cd
(change directory) to the folder
containing your source image folder,
and give a folder path relative to where you are working:
$ cd /MY-STUFF/camerauploads $ python3 /Code/thumbspage/thumbspage.py Images folder path [. or dir] (enter=.)? imagefolderhereOr, run the program anywhere and give an absolute path to your images folder:
$ python3 /Code/thumbspage/thumbspage.py Images folder path [. or dir] (enter=.)? /MY-STUFF/camerauploads/imagefolderhere
Absolute paths are generally required when running thumbspage from an IDE such as PyEdit, if they run code in the program's folder. Also see your file explorer's options for copy/paste of a pathname to which you've navigated; it can often avoid having to type a long pathname at thumbspage prompts.
As you might expect, the thumbspage.py
script's path in console command lines
can be relative or absolute too, depending on your console's current directory. For instance,
py -3 thumbspage.py
suffices to start the program on Windows if run in
the thumbspage install folder, though your images folder will reside elsewhere—and
may be relative or absolute:
> cd thumbspage-install-folder > py -3 thumbspage.py Images folder path [. or dir] (enter=.)? C:\MY-STUFF\camerauploads\imagefolderhere
If you'd like more background on command-line use, you'll find both online and brick-and-mortar resources that go into more detail. Here, the next section moves on to show you how to make your galleries more unique.
The most common thumbspage customization options are available as console inputs on each run, as described in the preceding section. This section covers additional customization options enabled by file edits.
A set of additional customizations are available as Python settings in file
user_configs.py
.
See that file for its options, documentation on their roles, and
their preset values.
As examples, that file defines Unicode encodings; gives the names of the
generated index page and thumbs folder; turns subfolder-link lists on
or off; as of 1.5, configures most colors; and as of 1.6, allows images
to expand beyond actual sizes, and users to control auto-rotation of
images (described ahead). Versions 1.7 and 2.0
add additional options you'll find later in the file.
For more custom behavior, add unique HTML code to the top and bottom
of the index page by placing it in files named HEADER.html
and FOOTER.html
, respectively, and storing these files
in the images folder alongside your images.
You can use one, both, or neither of these files; if not present, generic
HTML and text is automatically generated in the index page around the
thumbs table. For details on how to code these files, see the demos in
folder examples/
here. In brief:
HEADER.html
<!doctype>
,
<html>
, and the <head>
section with all the usual
components if used by your index page—<meta>
tags for mobile viewport
and content-type Unicode encoding, <style>
, <title>
,
<script>
, and so on.
This preamble should be followed by the start of the <body>
section
with any informational <p>
paragraphs or other content.
<body>
margins
(e.g., to appease curved mobile displays), as in
this demo's
source code; default headers do not, because there isn't any substantial text at page top.
<meta>
tag should generally be the same as the outputEncoding
setting in the
user configurations file, because the latter is used when
saving the whole index page, including its generated thumbnails table. See version 1.3
release notes ahead. The UTF-8 default is recommended in all contexts.
FOOTER.html
</body>
and </html>
.
For example, footer content might include navigation-toolbar
links,
or informational text;
see both links for examples.
Subtlety: galleries which both use a custom footer and enable a floating Top button for the index page may also need to allow for extra end-of-page blank space in the footer's code to prevent the button from covering final non-fixed content; see the version 2.0 note ahead.
The generated thumbnail index table's code is self-contained, and requires
no support code in a custom header.
Conversely, a HEADER.html
file can use CSS style code to
alter the thumbs table in ways not supported by basic user settings (e.g.,
to tailor index-table font).
For examples of this technique, see the docstring in
user_configs.py
, as well as
the online demo site
here.
As of version 1.6, viewer pages can also be changed arbitrarily by editing
the template file
template-viewpage.html
in this program's install folder
(use "view source" in your browser to see its code).
For example, such edits might add site icons or navigation widgets
specific to your usage or site.
Edit with care (this file's code is fragile!) and rerun thumbspage
to use your customized template.
As of version 2.0, you can also customize the optional floating
Top button of index pages, by editing the new source-code file
template-floatingtop.html
,
which host the button's implementation code. This isn't normally
necessary, because nearly every property of the Top button can be
customized with settings in the configs file, per the 2.0
change note.
To tie together the ideas covered so far, the following sketches the structure of an images folder processed by thumbspage, with generated parts in bold font and default names and behavior applied:
Your image source folder/ Your images' files... Optional HEADER.html Optional FOOTER.html index.html _thumbspage/ Thumbnail-image files... Viewer-page files...
Open the generated index.html
file to view the thumbspage gallery,
and package the entire images source folder to distribute.
In addition, some files in the thumbspage install (unzipped) folder are available
for user customizations:
thumbspage install folder/ Implementation files... user_configs.py template-viewpage.html template-floatingtop.htmlEdit
user_configs.py
and the HTML template files
as you like for your galleries. Not shown above, your image folders may also
contain subfolders that might show up in bullet lists on the index page;
but to explore special cases like this we have to move on to the next section.
This section collects assorted pointers for thumbspage gallery builders. Though arguably random, it covers some of the most common issues and border cases that may arise when using the program. Notes you'll find here (be sure to also browse Version History for tips omitted in this section):
Update: as of version 1.7, viewer pages are even more functional—with info-dialog popups on filename taps, and easier Raw-display access via image clicks—and repair some former large-font issues (see the release notes). These pages are now broadly recommended.
Update: as of version 2.0, "Raw" button taps are now fully replaced by image taps, and Auto and Full buttons provide slideshows and one-page fullscreen, respectively; see 2.0's release notes, and use viewer pages.
"
), especially if they are to be
viewed on multiple devices. By contrast, galleries uploaded to a web server need
satisfy only their server's filename rules.
Also note that because filenames used on labels are not line-wrapped, their width largely determines column spacing on the index page. As a rule of thumb (pun intended...), use shorter filenames for narrower columns.
Update: on a related point, version 2.0 now wraps or scrolls absurdly long file and folder names; see the release note.
That said, thumbspage galleries can display only image types supported by both the Pillow library used to build their thumbnails, and the web browsers used to view their pages. While Pillow happily creates thumbnails for nearly every image type under the sun, web browsers are much more limited.
TIFFs, for example, yield correct thumbnails and display well in Safari, Edge, and Internet Explorer, but cannot be displayed by Chrome or Firefox. The upstart WebP format similarly is supported by Pillow (and hence by thumbspage builds); but support is an add-on in some versions of Edge and still growing in Safari (and hence in thumbspage views). See this shot for current results in Chrome (top left), Firefox, and Safari (right).
Because of these constraints, the support story today reads as follows:
Some browsers may ask to auto-open some exotic image types in another program, but this isn't the same as in-page support. If your TIFFs (or other) images don't display in a browser you use, your best recourse is to convert them to a more widely supported format for use in thumbspage, such as PNG or JPEG. A "Save as" in your local image editor will generally suffice. For a demo of common supported image types, see examples/mixedtypes. For more on browser image support, try this page or this search.
Also note that the above pertains only to image display. Support for image metadata—of the Exif-tag sort displayed by info popups—varies by image type too. JPEGs are generally tag rich, for example, but PNGs, TIFFs, and WebPs may be more spotty, and image libraries may not recognize newer formats. thumbspage collects metadata where possible, and omits it elsewhere.
One browser-specific caution: a somewhat dated Safari (2015's version 9) on Mac OS has been seen to crash altogether when trying to display a PBM in the mixed-types demo pages, for unknown reasons that are well beyond the scope of the thumbspage project. Safari updates, image conversions, and other browsers are the suggested remedies.
Exception: subfolders named with a leading _
or .
character are not included in the bullet list; the former is considered developer
private per Python convention, and the latter is hidden per Unix convention.
See version 1.1's note ahead for more subfolders lists in general,
and version 1.7's update to skip .*
subfolders too.
.../folder
and .../folder/
work only when a web server is present. Use the more complete and explicit form
...folder/index.html
to also (or only) view results offline.
To link to individual images, use either their image-file or viewer-page URLs;
the latter gives access to the gallery and its formatting. In sum:
...folder/index.html # the index page ...folder/image.jpg # an image itself ...folder/_thumbspage/image.jpg.html # an image's viewer page
Because rotation changes the source
image file in-place for browser inline display, the original image
is by default first saved with a .original
filename
extension in the source folder as a backup copy. See
user_configs.py
for
settings that allow users to disable this feature and/or its backups;
the included simple
utility script
that restores all originals from backups;
and 1.6 release notes in Version History ahead
for more background.
Conversely, folder cleaning is not required if images are only added to the images folder—thumbnails will be made for new images only, and thumbs for previously added images will be retained. This can save substantial time when extending large image folders. But when in doubt, clean; this script's work is done once, before any views.
_thumbspage
by default, in
user_configs.py
. It is not
named with a leading .
character to make it a Unix hidden file,
because hidden files are rude (you should really see what programs do to
your computer), and may be skipped by some compression and backup
programs (e.g., see ziptools and Mergeall at
learning-python.com).
The default name can be freely changed to be hidden if .
tradeoffs are acceptable in your usage (but you didn't hear that here).
The issue: when published online, thumbspage's thumbnail-index pages load quickly, but its image-viewer pages must download images in full before scaling them to browser windows. As a consequence, some large images in thumbspage galleries may download slowly on some servers or clients. In early 2020, for example, large (e.g., 2-6M) image transfers on the business-hosting server that houses thumbspage were usually very quick (e.g., 2-3 seconds), but sporadically throttled down to an outright crawl (e.g., 20-30 seconds). It's unclear if this speed hit was due to traffic on the server itself or the broadband and cellular client networks accessing it, but similar delays may occur in other contexts. Such delays can obviously be discouraging to site visitors, and posting large images in general might even be rude to users with limited or metered bandwidth.
The remedy: if the full-size images in galleries you build with thumbspage load too slowly, your recourses include moving to a faster hosting server, and downscaling the size or quality of very large images. Of these, downscaling images may be easiest, cheapest, and politest. The Pillow library, for instance, has tools that you can leverage in a Python script to reduce image filesize by scaling down image dimensions and quality. Better yet, get the new shrinkpix program for a precoded solution that uses Pillow this way to reduce the size of one or all image files in your website. However you opt to shrink, be sure to regenerate thumbspage galleries to reflect your newly shrunk images' information displayed in info popups.
A faster, and perhaps dedicated, server may help too, but not for users on metered connections or slow client networks; for such visitors, smaller images (e.g., < 500K) may be the only fix, and even then will solve speed issues only if network throttling isn't extreme. The website hosting thumbspage has resisted downscaling its images in the past for the sake of both image quality and usage demonstration, but at this writing is in the process of shrinking images globally. Downscaled or not, if images here are still slow for you, you can always view most of this site's thumbspage demos by downloading and unzipping to your machine and viewing locally. This includes thumbspage's own examples—fetch its full package for quick off-line image views.
In a perfect world, online images would by now be immune to the scourges of metered access and traffic bottlenecks. In the world we inhabit, profit will probably always trump societal need, and the web's mass popularity will probably always best its technological progress.
Update: per testing so far, shrinking large images with the shrinkpix program mentioned above both removes observed slowdowns, and seems to yield faster views. The primary downsides are minor and rare reductions in full-size image quality (described in shrinkpix's caveats), and potentially degraded quality for thumbnails. thumbspage 1.7 solved the latter issue by converting some images to "RGBA" color mode temporarily when making their thumbnails; this produces results as good as for unshrunk originals, and has the added benefit of improving thumbnail quality for some unshrunk GIF images too. See the 1.7 update note and shrinkpix docs for more details.
Update: with the benefit of hindsight, most of the speed issues related to images at thumbspage's site stemmed from web-host speed, not image-file size. Speed issues were finally resolved in full in Spring 2020, by moving this site from its former GoDaddy host to an AWS Lightsail VPN server. The speed increase for thumbspage galleries was staggering, and far surpassed the gain from reduced image-file size (which was so overshadowed by host sloth, that it could not be reliably measured). Still, shrinking images was important for users on metered-bandwidth clients; images at this site are all now 500K or less (and average about half that size), and that's much less to ask of casual browsers than the former 6M bandwidth bombs.
Update: for completeness, it's worth noting that you can shrink a website's images dynamically with Apache's mod_pagespeed (a.k.a. PageSpeed) module. This ambitious module attempts to optimize size of fetched images by automatically resizing and caching for future requests. Unfortunately, you have little control over this process—or the quality of the images it produces. Worse, this module also mangles page code for transmission (a rude nonstarter for sites that double as page-coding resources), and abruptly crashed this site's Apache server when applied by default by a Bitnami install stack (despite the module's experimental "Incubating" status). Your mileage may vary, but static image-size reduction seems a more deterministic—and safer—approach.
Besides its HTML galleries, you can also view and click thumbspage's
generated thumbnails in GUI mode (i.e., without a web browser), by
running the included tkinter
example from the
book
where parts of thumbspage first appeared. This GUI works on Windows
(any version), Unix (Mac OS and Linux), and probably on
Android (using a launcher in the Pydroid 3
app)
as follows:
c:\...\thumbspage> py -3.3 viewer_thumbs.py examples\unicode\images # Windows /.../thumbspage$ python3 viewer_thumbs.py examples/unicode/images # Unix
This is, however, a very basic and limited viewer GUI (and today even confesses as much). The book's later PyPhoto example is a much more useful GUI, with navigation, image scaling and zooms, and scrollbars for both indexes and images (as seen here and here). More recently, a much-upgraded PyPhoto is now available standalone, at the following site (it's a bundled PyGadget):
http://learning-python.com/pygadgets.html
Also note that thumbspage itself can serve as an image viewer too. Because its results can be viewed both online and offline in any desktop or mobile browser, they can be used for same-device viewing; your browser becomes the GUI.
Finally, a word from the legal department. thumbspage has been tested extensively and used successfully on all types of photo collections, and will likely perform well on yours too. It is provided freely because it can help you view and display your image libraries. Given the many ways that computers can fail, however, a word of caution is in order:
By design, this program will modify your images source
folder in-place. It adds an HTML index-page file (by default named
index.html
) and a subfolder with thumbnails and HTML viewer pages
(by default named _thumbspage
), and as preconfigured rotates any
tilted images after saving backup copies of their originals with
.original
extensions. Run this program on folder copies if you
don't want it to change your valued photo collections directly.
All that being said, keep in mind that you can easily delete the file
and folder added; original versions of rotated images can be easily restored
with the included script
restore-originals.py
;
and both image rotations and their backups can be disabled in
user_configs.py
.
Moreover, if you always run thumbspage on a copy of your source
images folder, your originals will never be changed by the program in any way.
Still, the importance of your photos merits a complete understanding of
any tool that modifies them—this one included.
Like much in life, thumbspage improves with time and experience. This section describes changes made in thumbspage releases, most recent first. It goes into implementation details that may be primarily of interest to developers, though users can find additional context here too (and the border between thumbspage users and developers is porous at best).
Before jumping into the releases, here are two global admin notes up front.
First, for readers of code—you can generally find changes made to code by searching
source files for a version number enclosed in square brackets. For example, looking
for [1.6]
will turn up version 1.6 code changes in most
code files; a release year or date in brackets may also work in
some contexts, especially for older releases.
Second, for detail-minded fetchers—if you find that a zipfile or its contents are dated later than the current version's latest release date, it just means that trivial non-code fixes were applied in a rezip. This almost certainly implies minor doc typos (which have a nasty habit of hiding until just after a release). Significant changes, and all code changes, are always called out with a new release date or number here.
For readers who want to save a Top click, here's an index to the version coverage in this section:
thumbspage 2.0, released in final form on October 5, 2020, adds an automatic slideshow and optional one-page fullscreen on image-viewer pages; an optional floating Top button on index pages; a custom dialog and device line for info popups; improved overflow styling everywhere; and a resolution to an iOS Chrome history bug.
This version also changes the UI significantly (hence the 2.0): index pages grow an optional Top, and viewer pages replace Raw with Auto, sprout an optional Full, modify toolbar layout, and change info-popup appearance and content radically. To sample 2.0's new displays, see either the live demos on its hosting site, or the latest 2.0 screenshots. The latter supersede captures from older releases which have not yet been retaken (alas, this task may be preclusively monumental).
The rest of this section drills down on each of 2.0's changes listed in the table below. For prior visitors: version 2.0's enhancements in this table were rolled out over four 2020 releases. Items 1–6 were included in the June 26 initial release; items 7–11 were added by a July 10 point release; items 12–13 were addressed in a July 18 point release; and items 14-15 appeared in the October 5 final release. All are part of thumbspage 2.0.
In complete terms, 2.0 adds the following upgrades:
Though the new slideshow is largely automatic, the delay between image pages
can be configured on a per-gallery basis at build time: see the new
autoSlideShowDelayMS
setting in the
configs file. To keep the interface simple,
the delay is not changeable by gallery users, though they can turn slideshows
on and off at any time and on any viewer page.
In viewer-page toolbars, the new Auto button replaces the former
Raw button,
which grew redundant with the new image taps added in
1.7; tap the image for
the former behavior of Raw (and mind any older screenshots that still display it).
The new Auto starts and stops the show's progression on demand, and image slideshows are paused when a gallery is exited; they may be resumed on both returns with a browser Back and new selections from the index page, and Next and Prev may be used to skip images.
Technically (or at least conceptually), Next and Prev, like any page exit, cancel a scheduled timer event automatically, but the new page schedules a new timer event when loaded; the net effect is to continue the show from the new page. Slideshows also generally run in backgrounded tabs, but this is prone to vary per browser. This is all subject to the peculiarities of browser back-forward caches (and may be partially broken on Safari), but largely works well on all the many browsers tested.
For more slideshow usage details, read the earlier viewing coverage. For additional implementation details, see the initial proposal ahead, or browse the viewer-page template's code. And for concise demos of the sort of code used for cross-page state and slideshow timers, see and run the pages in the example folders here and here.
Constraint: the new Auto slideshow requires JavaScript. If JavaScript is disabled in the browser, the Auto button is still displayed but has no effect, and users will be shown the no-JavaScript warning which also apples to dynamic image scaling and more. Given that thumbspage galleries now require JavaScript for info popups, image scaling, Auto slideshows, Full one-page fullscreen, and the next section's floating Top, users can be reasonably expected to enable JavaScript for a more rewarding UI experience. See also the viewing note for another take on this requirement.
The new Top button is intended for larger indexes with useful top-of-page content. It won't appear in small indexes with little or no vertical scrolling, and is generally unrecommended clutter for indexes that have nothing worth scrolling to at the top of the page. It's enabled by default nonetheless, because some pages do have useful top content, large indexes require substantial scrolling on mobiles, and users may wish to start the new automatic slideshow at the first image in the gallery (see the prior section).
See the configs file for settings that customize the new Top button. In short, gallery builders can:
floatingTopEnabled
floatingTopAppearAt
floatingTopSpaceBelow
floatingTopFgColor
and floatingTopBgColor
template-floatingtop.html
which hosts its code
In addition: galleries that use a custom
FOOTER.html
file may need to
add space below a final non-fixed content element, to prevent Top
from overlaying and hiding it. This is accommodated automatically in default
index footers with a style of this sort, to leave blank space after the final
paragraph:
<P style="margin-bottom: 80px;">
The effect is captured
here.
Depending on its content, a custom footer may wish to do
similar.
Naturally, <br>
s and padding-bottom
can have
the same effect and a surrounding <div>
may help, and
be sure to also allow for horizontal scrollbars on desktop browsers that
display them if your index page needs to care. As
generated, Top's bottom-offset setting prevents it from encroaching on
lower fixed toolbars, but not end-of-page text; add space as needed to prevent
overlays at the bottom, especially on small mobile
displays.
Constraint: the new Top button requires JavaScript to be enabled in the browser. If JavaScript is disabled, index pages still work normally and as before 2.0, but do not display a Top even if one is generated. No warning is issued in this case, because the index page still functions in full with scrolls (where they are required at all).
showFullscreenButton
in file
user_configs.py.
Where supported, Full does expand and collapse the viewer page on taps. When on, fullscreen fills the whole screen, dropping system status and navigation bars, along with all browser bars (even in some contexts where manual options don't). This yields a somewhat enlarged image (plus an annoying message in some browser). Crucially, though, it lasts just for one image page: any navigation, including a Next and an Auto slideshow transition, cancels fullscreen jarringly.
There's no way to work around this, because fullscreen by spec must be requested only from a short-running, user-initiated event (read more about its API here). This is presumably to avoid phishing spoofs, but limits options for browser-based GUIs, and a complete redesign of thumbspage galleries to avoid page changes on image navigation is a nonstarter.
Users may be better served by browsers that have manual fullscreen options which persist across page changes and span Auto slideshows. See the earlier viewer note for manual tips, and the viewer-template file's code for implementation details. Manual fullscreen doesn't work everywhere, but it generally beats thumbspage's Full where it does. On browsers without a manual fullscreen, though, Full may still be useful as a zoom. Enable or disable as you like; it's your gallery.
Constraint: the new fullscreen display requires JavaScript. If JavaScript is disabled in the browser, the Full button will not be displayed, even if it is enabled in the configs file (its presence is decided by generated page code), and users will see the usual warning, which also applies to Auto buttons, filename-tap info popups, and dynamic image scaling on viewer pages.
overflow-wrap: break-word
on the <h1>
title;
the result for pathologically long cases looks like
this
overflow-x: auto
(among others) on the toolbar's elements;
here's the result for long names and large fonts
before and
after scrolling
Shown in the last screenshots above, the filename at the top of viewer pages
was already set up to scroll on overflow too, and required no changes.
Also note that the first item above
applies to generated default headers only; index pages using custom
HEADER.html
files are
responsible for wrapping any title content manually if needed,
with similar techniques.
Though important, the two new stylings are mostly just for unusual border cases having either very large fonts, or very long filenames with no breakable characters, and generally apply only to very small windows or devices. On mobiles, for example, too-long filenames that exceed viewports make the entire page scroll horizontally, and too-big toolbar content can run together.
In typical usage, long filenames in HTML are automatically wrapped as needed
at embedded characters like -
, and the viewer-page's toolbar
content is too short to require a scroll. In unusual cases, though, the new
stylings ensure that long unbreakable content won't break viewports, and toolbars
won't lose their spacing.
For additional implementation details, see the code in the index-page generator and viewer-page template.
Because this was no worse that thumbspage's work-around, Version 2.0 initially changed the related config-file setting's default to skip the 1.6 work-around. Happily, as of Chrome 83 in June 2020, the history bug appears to have been fixed in full: thumbspage navigation pages are no longer stacked or retraced, and the initial 2.0 change to skip the temporary work-around suffices to adopt the fixed behavior. Hence, thumbspage history now works the same on iOS Chrome as on all other browsers; this issue is closed; and no further attention is required (unless and until it breaks again).
<head>
section of both default-header index pages, and all image-viewer pages,
as a target for search-and-replace:
<!-- Plus analytics code, custom styles, etc. (replace me) -->
This might be used by a site-publishing script to insert analytics code
or custom fonts, and may serve as a lighter-weight alternative to custom
HEADER.html
files for index pages,
and template-file edits for viewer pages.
alert()
dialog
with a custom modal
dialog—which
is really just a full-page
overlay with opacity, plus a display box within it, all shown and hidden on
demand. Most of the dialog's implementation code (CSS, HTML, and JavaScript)
is at end of the viewer-page template file;
the former callback handler for filename taps still formats the message using
Python replacements and JavaScript DOM data.
This naturally invalidates some former info screenshots, but 2.0's captures are all current. See especially its info-popup subfolder gallery for captures of the new dialog in action.
So why go to all the trouble of a custom dialog? For one thing, some browser
vendors discourage alert()
usage,
and a few browsers (e.g., Firefox) even treat it as a quasi threat, asking users if they
wish to
silence
it (odd, that, for a tool that's been a standard since the 90s).
Much worse, mobile browsers format alert()
text badly with wrapping,
and iOS 13's Hide Toolbars mode can even botch it altogether, displaying text outside
the dialog box.
All told, the new custom dialog offers a number of advantages, some of which work around browser-specific limitations. It:
The new dialog also scrolls its page vertically if needed for mobile landscape, and its use of viewer-page color settings makes for a more consistent appearance. Its only potential downside is that it won't pause Auto slideshows, but this may also be a bonus to some observers.
While the new dialog comes with added complexity, its UI improvements are well worth the cost—and better accommodate some of the following notes' content-expanding changes.
Depending on image content, the new info line may be present or not, and is formatted conditionally as follows:
Model
tag of an image's
metadata (part of the tag set collectively called Exif in this document; see the
standard); it's normally
present in shot photos and scans, but not in screenshots or drawn images.
Make
tag is appended if
it is present, just one-word long, and not redundant with a word already in the device
string; else it's judged extraneous and omitted.
Software
tag's value is reported instead
when available; it's recorded in some drawn images, but this varies by program.
In all cases, the device-or-software string is truncated if it exceeds a fixed length,
and characters special in JavaScript strings are replaced with ?
.
Here are examples of the new popup field for images with
device,
device and
brand,
no device but
software,
and neither type of maker
tag.
For additional examples, see the
info
gallery,
and images in this
live demo.
Along with the other curated info-popup fields already displayed, the new maker line seems of primary interest to content creators. While additional Exif-tag display is nearly open ended (e.g., GPS data could spawn maps), thumbspage aims to strike a balance between image information and interface simplicity.
Here are examples of the three labels in action for photo, scan, and drawn images. 2.0 also considered omitting the date-of-origin line altogether when this date is unknown (similar to the new conditional Device/Software line of the prior note), but opted to retain it because the filesystem-based Modified line seems otherwise confusing: it might reflect origin or edit, but neither is implied.
Also note that some filesystems record a creation date explicitly (along with modification date), but it's generally unusable for thumbspage's cross-device info displays. Its support varies widely per filesystem (and not just platform), and even where present may not reflect actual creation date after copies and other changes. When in doubt, "unknown" is better than inaccurate. For more details, see Python's os.path.getctime() and the usual web searches.
w
and h
, respectively. For example,
the popup's new content for a scan looks like this, with dimension
lines bold here:
Digitized: 2013-04-14 @15:15:55 Modified: 2020-03-05 @15:46:57 File size: 482,451 bytes Image size: 2,944w x 2,088h Display size: 985w x 699h Device: CanoScan 8800F (Canon)
Trivial, perhaps, and English biased, certainly, but without the suffixes, it can be way too close to call in images that are nearly square.
window.name
JavaScript DOM variable to implement cross-page state. While this clearly
smacked of a hack, it was simple, could not be disabled by users, and was
supported on every one of the dozens of desktop and mobile browsers tested.
Unfortunately, this is now known to fail on Safari, only, when
viewing galleries offline on the local file system: the slideshow stops
after a single image flip, because state is not retained across pages.
This likely pertains to desktop Safari on Mac OS only, and was
seen on its version 13 in particular; mobile
Safari on iOS has no clear notion of local views
(and later evidence seems to exonerate mobile in full).
This issue wasn't detected in earlier testing, because slideshows work
correctly on Safari when viewing online galleries uploaded to remote servers.
In local-file mode, though, Safari apparently disables window.name
or reserves it for its own internal purposes—and
breaks any client code that wishes to make use of it in the process.
This may be yet another example of the
opinion-based rudeness which has grown sadly common in the software field
today, but that's a topic for another venue.
To make slideshows work on Safari too, thumbspage now uses HTML5 session
storage in all browsers to implement the cross-page state
required for the Auto toggle. As a fallback, it still uses the former
window.name
scheme when session storage is unavailable
or disabled; older IEs don't support it, and some browsers allow
users to manually disable it. Here are the highlights of the new
JavaScript template code; see its file's source
for the full, commented version
(and yes, this doc would rather show Python code, but vendors of web
browsers—and mobile devices—have rudely dictated otherwise):
var showDelayMS = %(SLIDESHOWDELAY)d; var toggleKey = 'thumbspageAuto'; var toggleOn = 'show-timer-on', toggleOff = 'show-timer-off'; function hasSessionStore() { try { sessionStorage.setItem('testkey', 'testval'); return (sessionStorage.getItem('testkey') == 'testval'); } catch (err) { return false; } } function autoClick() { if (hasSessionStore()) { if (sessionStorage.getItem(toggleKey) == toggleOn) { sessionStorage.setItem(toggleKey, toggleOff); clearTimeout(showTimerID); } else { sessionStorage.setItem(toggleKey, toggleOn); showTimerID = setTimeout(function() {navClick('%(NEXTPAGE)s');}, showDelayMS); } } else { if (window.name == toggleOn) { window.name = toggleOff; clearTimeout(showTimerID); } else if (window.name == toggleOff || window.name == '') { window.name = toggleOn; showTimerID = setTimeout(function() {navClick('%(NEXTPAGE)s');}, showDelayMS); } } } if (hasSessionStore()) { // on page load rescheduleAuto = (sessionStorage.getItem(toggleKey) == toggleOn); } else { rescheduleAuto = (window.name == toggleOn); } if (rescheduleAuto) { showTimerID = setTimeout(function() {navClick('%(NEXTPAGE)s');}, showDelayMS); }
With this change, Auto slideshows progress as expected on Safari and all others.
Session storage, available via both globals and window.
properties,
works the same as window.name
: it's unique per tab, applies to all pages
opened in a tab, and is removed on tab or browser closure. It differs between http
and https
accesses, but this is irrelevant for the relative URLs of Auto slideshows,
and sites can rewrite the former to the latter automatically. For more background
details, see the off-site coverage
here
and
here.
The lesson here may be that something which feels like a hack probably is one—and browser vendors may use it as such even if you don't. More fundamentally, this is a reminder that web development today requires supporting multiple, incompatible, constantly morphing, and opinion-driven implementations of what is supposed to be a reliable standard. That's not engineering, and it's about as far from developer friendly as it could be; fix, please!
In testing so far (using desktop Safari 12 and 13 on Mac OS Sierra and Catalina), these stops and crashes were seen to occur only for the following protocol-switching navigation sequence (spoiler: per the epilogue ahead, Safari Auto stops may also happen on domain switches in all-remote navigations, and this also seems limited to desktop Safari):
file://
)
https://
)
file://
)
Safari in this context wrongly clears the Auto toggle in HTML5 session storage to
JavaScript's falsy
null
, thereby turning the slideshow off.
Most of the time, simply tapping Auto suffices to restart the show.
Occasionally, though, and with no discernable cause, Safari botches the
gallery's viewer page completely in this event, displays the following
notorious
error in a popup
dialog,
and may need to be restarted in full:
"The operation couldn't be completed. Operation not permitted" (NSPOSIXErrorDomain:1)
Very oddly, none of this appears to happen when Safari's JavaScript console is open, which suggests a deep structural bug in the browser. Moreover, none of this occurs in any other tested browser—the Back stops and crashes were observed only on Safari. Unfortunately, it's unknown if this is limited to desktop Safari, because iOS's harsh access constraints discourage or preclude local-file galleries on mobile altogether (but watch for later clues in the epilogue).
In the possibly related department, it's worth noting that, both with and without the change to use session storage, Safari also produces an uncatchable exception on Auto image switch, which leaves the following suspicious error message in the JavaScript console:
Not allowed to load local resource: file:///favicon.ico
This happens only for local-file views just like the initial Auto-slideshow failures, but there appears to be no cause and effect relationship between the two: the exception also occurs on Next and Prev taps, and isn't remedied by disabling local-file restrictions in the Develop menu or pasting a valid favicon into the folder.
Just as strangely, a Back in Safari, only, does not run (or properly run) resize handlers in remote galleries: if the window is resized while away, the gallery doesn't catch up until the next page in the show or a forced reload. By contrast, a Safari Back does resize local-file galleries, but also kills their shows by clearing storage (and sometimes crashing). No other tested browser has these issues.
thumbspage tried multiple work-arounds to address the Safari failures—including
moving timer rescheduling from top-level code to pageshow
event handler;
forcibly cancelling timers on page unloads; and forcibly reloading the
page when reshown. Some of these were meant to address the theory (really,
wild guess) that failures stemmed from the browser's
back-forward cache.
For example, the first of these used top-level JavaScript code of this form:
function autoContinue(event) { // former top-level reschedule code of prior note } try { trace('run timer-reschedule code deferred'); window.addEventListener('pageshow', autoContinue); } catch (err) { trace('run timer-reschedule code immediately'); autoContinue(null); // run now in older browsers (or: window.onpageshow) }
None of these attempts helped as coded. Whether overzealous security constraints, cache optimization schemes gone bad, or outright bugs, something is clearly amiss in Safari local-file views that use session storage and timers.
As a more radical alternative,
HTML5 localStorage
, unlike sessionStorage
, might
not be erroneously cleared by Safari on Back, but this may not fix the crashes,
and is too broad and persistent to be used in any event: a site's slideshow toggle
would then be machine
global,
enduring to apply to later shows in ways users seem unlikely to expect—or
appreciate. Downgrading user experience this way for every browser
is not a valid fix for the foibles of one.
The upside here is that slideshows do work well in all other browsers, and work well in Safari most of the time too, after moving to session storage. They're currently known to fail only on switching from and to local galleries in specific Safaris; this is a rare—and even unlikely—use case. Nevertheless, given that this Safari glitch has all the hallmarks of a browser-vendor bug, and has stubbornly defied all work-around heroics to date, it will have to remain an out-of-scope caveat for thumbspage 2.0.
If this Safari quirk crops up in your thumbspage adventures, your best recourses are to tap Auto again to restart the show when possible; use an alternative browser that's less broken; or lobby Apple to fix its junk.
Epilogue:
after the above was written, further usage revealed that a Back in Safari sometimes
stops Auto slideshows for all-remote (i.e., online) navigation too, if users leave to another
domain (i.e., site) and return to the slideshow page.
That is, Safari's Back glitch may also be triggered by a domain switch. Just as
importantly, all-remote stops appear to happen only in the desktop Safari browser—not
in mobile Safari on iOS 13, and not in a dozen other browsers tested among Windows, Mac OS,
and Android.
In more detail, Safari, only, is now also known to stop an Auto slideshow running
in an https://
page, after navigating to another https://
page
at a different domain, and returning to the https://
slideshow page with
Back. This does not happen for all such remote switches, however. Curiously—and
perhaps entertainingly—a slideshow running at https://learning-python.com
rarely stops when switching to and from https://apple.com
, but reliably
does when the navigation target is https://google.com
.
HTML5 session storage should endure for all like-kind pages visited in a browser tab (or window).
It's specific to page protocol and domain by definition
(see
here,
here, and
here),
so it's expected to start fresh when navigating from a file://
page to https://
,
or to a new site in https://
.
But a prior page's session storage should also be restored and/or intact when a Back returns
in both of these contexts, and neither a protocol- nor domain-switching Back should ever
crash the browser. Only Safari bears these warts.
There's certainly more to this story, but forensics sans code is guesswork, and we'll
have to leave this bug tale here. Whatever the underlying cause, this is clearly
an issue for Apple to resolve;
until it does, the work-around for thumbspage users remains an Auto-press restart,
or an alternative-browser install.
Today's desktop Safari seems far too prone to toss its cookies
(or at least its session storage...).
DecompressionBombWarning
message now issued
senselessly by the underlying
Pillow library for all large
images.
Because the fix does not in any way alter the galleries which thumbspage builds,
none need be regenerated; this mutes one build-time message only.
Specifically, when building galleries with images larger than 89MP, the Pillow library by default prints a single DOS (denial of service) warning message in program output that looks like this (with line-breaks added here for marginal readability):
/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/PIL/Image.py:2797: DecompressionBombWarning: Image size (108000000 pixels) exceeds limit of 89478485 pixels, could be decompression bomb DOS attack. warnings.warn(
This baseless warning is completely harmless, and does not impact thumbspage results; large images work fine in thumbspage galleries whether this message appears or not. But it's also stupidly excessive, and needlessly confuses users of this and many other Pillow-based programs. The message's over-strong language may even scare some users over a threat that doesn't exist.
The warning was first seen for valid 108MP images shot on a Galaxy Note20 Ultra smartphone in 2020, and will crop up for all the increasingly common large images created by newer devices and tools. While the fix to silence the spurious warning may be trivial for program developers, program users have no recourse, and are left wondering if programs are buggy—or worse!
To see the fix's code for yourself, search for Sep-2020
in the
source.
Though simple, a "fix" is often far easier than a release.
At thumbspage's host site, multiple programs were impacted—including
thumbspage,
tagpix,
shrinkpix, and
PyPhoto—and each
required time-consuming retesting, repackaging, and redistribution.
This warning might be useful in some contexts, but it clearly should have
required an enable, not a disable.
Unfortunately, opt out is a regular by-product of opinion on overdrive.
Lesson: open-source agendas have consequences for others, and "batteries included" development entails substantial trade-offs. While this program could not easily exist without libraries like Pillow, it's also woefully dependent on them—even when they serve as platforms for personal preferences and subjective changes that stomp on the work of others.
So don't be rude out there.
Postscript:
though scantly documented, it turns out that Pillow later turned the
warning described here into a full error for images larger
than twice the warning's size limit. Though unlikely, this error takes
the form of an exception that will cause client programs to fail or terminate.
Despite this, its only mention seems to be in an obscure
release note;
whose details contradict an earlier obscure
release note;
and require studying Pillow's
source code
for full fidelity.
To avoid large-image Pillow errors, thumbspage's warning-silencing code has been
updated to use a new and broader fix
that sets Image.MAX_IMAGE_PIXELS
to None
—which will,
of course, suffice only until Pillow tightens the screws again. Large images are not
all attacks, and their creators are hardly ever criminals; unless it really
means to anger programmers and insult users, Pillow should really think
about making this check both opt in and better documented.
Summary: although thumbspage image rotations work well and as designed, recent findings suggest that it may be possible to implement thumbspage without rotations altogether, by propagating tags from sources to thumbnails. This hasn't yet been fully verified and no thumbspage changes have been implemented—mostly because programs that work don't generally need to be fixed—but this section provides background on non-rotation alternatives.
Details: to get started, we first need to understand the current rotation policy. Given a tilted source image coded with a non-"normal" Exif orientation tag (of the sort commonly had from smartphones held vertically), thumbspage today follows an evolved procedure to reorient images for in-page display that works like this:
.original
copy
The first three of these steps can be turned off on a per-build-run basis by a user configuration; if so disabled, thumbnails aren't rotated either, just because they're built from unrotated sources.
This scheme arose in phases over multiple versions and releases. thumbspage 1.5 displayed images too naively to count; 1.6 added auto-rotation of tilted sources, and their thumbnails by proxy; and 1.7 added Exif orientation-tag update and propagation for rotated source images (thumbs are simply built from already-straightened sources). The combination of these yields the procedure above, which is harmless for builders and viewers, and ensures that both sources and thumbs display right-side up in all contexts, regardless of their original orientation, and despite the vagaries of web-browser support.
Like much in computing, though, there may be alternative ways to do this. Per later research, it now appears that thumbspage may be able to skip rotations for both source images and their thumbnails altogether, as long as sources' orientation tags are propagated to their thumbnails. A non-rotation option is too involved to cover in full here, and is outside the scope of version 2.0. But the facts behind this alternative merit a few words.
In short, the JavaScript DOM seems to account for image orientation automatically in both its image properties and display. Specifically, the standard's doc here says this on the subject:
The IDL attributes naturalWidth and naturalHeight must return the density-corrected intrinsic width and height of the image, in CSS pixels, if the image has intrinsic dimensions and is available, or else 0. ... Since the intrinsic dimensions of an image take into account any orientation specified in its metadata, naturalWidth and naturalHeight reflect the dimensions after applying any rotation needed to correctly orient the image, regardless of the value of the 'image-orientation' property.
In other words, the DOM's "natural" sizes in JavaScript are already corrected for right-side-up display. The doc doesn't explicitly state that images themselves will be displayed this way when rendered as in-page elements, but this seems the case for a handful of major browsers in recent tests so far.
Because thumbspage has used the DOM's natural width and height in its dynamic image scaling since 1.6, it sizes the image for display in a viewer page with orientation already "baked in." By contrast, version 1.5 used the Pillow library's build-time width and height, which are not corrected for orientation the way that the DOM properties are; the result is an unscaled image auto-rotated by the same browsers, but stretched into pre-rotation dimensions.
Given the DOM's definition—and assuming every browser of interest implements an arguably implicit reorientation for in-page display—thumbspage's own reorientation of the source image is not required. Reorientation of thumbnails may similarly be unnecessary, but only as long as the source's orientation tag is propagated unchanged to the thumb to force the browser's adjustment. In sum, the alternative non-rotating procedure would:
This seems simpler, and may be, but it still must propagate updated tags using the piexif library (and fix tags known to make that library fail); requires the image-info popup to get dimensions from the JavaScript DOM instead of build-time Pillow (else they may appear swapped); and adds a browser-support dependency that the current scheme evades (and these are almost always best evaded). On the other hand, the source image would remain unchanged.
The required tag propagation of step 2 in the alternative scheme wasn't considered in 1.6 because it wasn't implemented until thumbspage 1.7. Prior to that, thumbnails made from unrotated sources were askew just because they had no orientation tag (and rotated source images were askew if their tags were copied but not updated). Rotating the source solved the issue in 1.6, but may now seem overkill.
All of which sounds simpler in hindsight, but thumbspage's scheme arose
in piecemeal fashion over years, and cannot easily be modified today.
While it's possible to disable thumbspage's auto-rotations process by setting
autoRotateImages
to False in
user_configs.py
, this leaves thumbs
tilted: because they're currently built from source images without copying the
source's orientation tag, they will display askew. Hence, either the source
must be rotated first (as done currently), or code must be changed to skip rotations
and propagate source tags to thumbs. And any change must also be predicated on
universal browser support for reorientations.
For now, thumbspage's reorientation scheme works as intended, has been used
successfully for years, and incurs no penalties apart from the minor overhead
of .original
backups.
While rotations might be rendered superfluous with redesign and recoding,
it's difficult to justify fixing a program with such a positive marketing story.
In its role as example for learners, however, thumbspage follows a
full-disclosure policy—even when that means pointing out its own rooms
for possible improvement.
Stop the presses: the non-rotating alternative proposed in the preceding section proved to be unusable after testing revealed that browser support for reorientation of images displayed as in-page elements is still coming online, and nowhere near the universal level required even on the latest platforms. Hence, in the name of inclusiveness, thumbspage will continue to rotate source and thumbnail images itself, because some browsers don't.
Details: although Exif orientation support has appeared in some browsers by fall 2020, a more complete analysis reveals that this is a very recent feature that has been appearing slowly. Along the way, its course included a CSS property that aimed to force the issue, but had weak support and was deprecated in favor of automatic adjustment. While automatic reorientation seems on track to becoming the norm in years ahead, it won't be present in versions of some browsers released just one or two years ago, and may never be adopted by others.
As a sample of where this support lies today, the following table summarizes auto-rotation findings for desktop and mobile browsers, as compiled empirically by thumbspage's testing department in October 2020; a "No" in the leftmost column means the browser doesn't adjust for orientation when displaying images as in-page elements:
Rotates | Device | OS | Browser | Version | Released |
---|---|---|---|---|---|
No | Desktop | macOS 10.15 | Firefox | 70 | Oct-2019 |
No | Desktop | macOS 10.15 | Safari | 13 | Sep-2019 |
Yes | Desktop | macOS 10.15 | Firefox | 78 | Jul-2020 |
Yes | Desktop | macOS 10.15 | Chrome | 85 | Aug-2020 |
No | Desktop | Windows 7 | Chrome | 77 | Sep-2019 |
No | Desktop | Windows 7 | Firefox | 56 | Sep-2017 |
No | Desktop | Windows 7 | IE | 9 | (unknown) |
No | Desktop | Windows 10 | IE | 11 | (unknown) |
Yes | Desktop | Windows 10 | Firefox | 78 | Jun-2020 |
Yes | Desktop | Windows 10 | Chrome | 85 | Aug-2020 |
Yes | Desktop | Windows 10 | Edge | 85 | 2020 |
No | Mobile | Android 7 | Firefox | 65 | Jan-2019 |
Yes | Mobile | Android 7 | Chrome | 81 | Apr-2020 |
No | Mobile | Android 10 | Samsung | 12 | Jun-2020 |
Yes | Mobile | Android 10 | Chrome | 85 | Aug-2020 |
Yes | Mobile | Android 10 | Firefox | 81 | Oct-2020 |
Yes | Mobile | iOS 13 | Safari | 13 | Sep-2019 |
Yes | Mobile | iOS 13 | Chrome | 84 | Jul-2019 |
For a more graphical look at these results, open the examples' thumbspage-screenshots gallery. As you can see, auto-rotation has come to some browsers in just the last year, and others still don't support it at all in fall 2020. On both desktop and mobile devices, current and recent versions of widely used web browsers still show orientation-coded images askew.
Which nicely captures a dilemma constantly facing engineers who work in a field as dynamic as software. Dropping its own reorientation might make sense if thumbspage could restrict its scope to just the latest-and-greatest browsers and versions. As a program designed to build galleries inclusive of all viewers, though, this is not an option. Even for browsers that have added the required support recently, versions just a few years old will still be common for years to come (despite the best efforts of their vendors).
Because we don't live in a world that restarts afresh each release cycle, thumbspage cannot be changed to rely on browsers to reorient tilted images any time soon. While this might trim some program complexity, it wouldn't work for many thumbspage-gallery viewers today, and requiring browser updates or replacements just to view thumbspage galleries would both be too extreme, and almost certainly qualify as rude (see the prior note).
For hard-core readers: the following Python session demos one way to do
the source-to-thumb tag propagation which was tested but ruled out;
Pillow also has some support for reading tags and processing them as
a bytes blob (see reorientImage
in
viewer_thumbs.py
), but
little to match
piexif
when it comes to updating tags:
>>> from PIL import Image >>> import piexif >>> it = Image.open('2020-09-17__180904.jpg') # unreoriented thumb >>> es = piexif.load('../2020-09-17__180904.jpg') # source tags (unreoriented) >>> et = piexif.load('2020-09-17__180904.jpg') # thumb tags (none yet) >>> es['0th'][piexif.ImageIFD.Orientation] 6 >>> et {'0th': {}, 'Exif': {}, 'GPS': {}, 'Interop': {}, '1st': {}, 'thumbnail': None} >>> >>> et['0th'][piexif.ImageIFD.Orientation] = es['0th'][piexif.ImageIFD.Orientation] >>> et['0th'][piexif.ImageIFD.Orientation] 6 >>> saveexifs = piexif.dump(et) >>> saveexifs[:20] b'Exif\x00\x00MM\x00*\x00\x00\x00\x08\x00\x01\x01\x12\x00\x03' >>> it.save('2020-09-17__180904.jpg', exif=saveexifs)
It's also worth noting that Pillow's ImageOps
module
has a call exif_transpose(image)
that straightens images
and simply deletes their Exif orientation tags, but this call
also discards all other Exif tags if its result is saved directly;
per warnings, seems to have even more issues parsing tags than
piexif;
and wouldn't make logistics any easier here in any event—regardless
of how rotation is coded, Exif tags must be propagated to sources for display,
and thumbs and sources must be saved to files in reoriented form because
both are displayed in thumbspage galleries as in-page elements that
aren't automatically righted by many browsers:
>>> from PIL import ImageOps >>> it = Image.open('../2020-09-17__180904.jpg') # unreoriented source >>> ir = ImageOps.exif_transpose(it) /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/PIL/TiffImagePlugin.py:588: UserWarning: Metadata Warning, tag 282 had too many entries: 2, expected 1 warnings.warn( /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/PIL/TiffImagePlugin.py:588: UserWarning: Metadata Warning, tag 283 had too many entries: 2, expected 1 warnings.warn( /Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/PIL/TiffImagePlugin.py:588: UserWarning: Metadata Warning, tag 34853 had too many entries: 6, expected 1 warnings.warn( >>> ir.save('rotated.jpg') # simple saves drop Exif tags >>> piexif.load('rotated.jpg') {'0th': {}, 'Exif': {}, 'GPS': {}, 'Interop': {}, '1st': {}, 'thumbnail': None} >>> piexif.load('../2020-09-17__180904.jpg') {'0th': {256: 4000, 257: 3000, 271: b'samsung', 272: b'SM-N986U1'... # and a lot more
In the end—and with all the evidence in—thumbspage did the right rotations thing in 2018 and still does in 2020. Its universally portable image reorientation still beats the partial support among browsers today (even if readers 10 years down the pike may not think so).
Version 1.7 was initially released in February 2020, and repackaged as point releases in March, May, and June of the same year. Here are the highlights of the 1.7 changes introduced by each release package:
Most 1.7 changes appear on generated image-viewer pages, and all can be found by
searching for [1.7]
in the system's code files as usual.
For examples of all the visual changes in 1.7, see its
screenshots page.
This section provides more detailed reviews of changes introduced across 1.7 releases. Because development of this version spanned half a year, its upgrades are many. For readers in a hurry, the later and longer reviews here come with summaries, and the following index provides fast access to this section's content:
To fix, 1.7 now calculates that actual space used by non-image page elements, instead of using a constant. See the results here, here, and here. As a bonus, this allows viewer pages to use all available space for the image, yielding a larger image display in most contexts. There is no downside to this change, and thumbspage users are encouraged to regenerate their pages with 1.7.
1.25em
in CSS).
Though very rare, this could cause the buttons to
run together
badly on some browsers
if the user applied a very large font setting. This was observed on only one
of dozens of mobile and desktop browsers tested, and at a font setting large
enough to break the design of many a site on the web, but was still subpar.
To fix this for the sole known offending context, and make it less likely to occur in general, 1.7 abandons the prior font scale up for toolbar buttons altogether. See the result here. As a bonus, this allows more space for the image, yielding a larger image display on all mobile browsers tested. The only potential downside to this change is slightly smaller toolbar buttons on mobile devices, but the new button size is more symmetric with the filename's font (see the next note), and users can still scale up text as desired.
Note that both this and the preceding change invalidate some examples screenshots in trivial ways: toolbar buttons are now slightly smaller on mobiles, and the image display is slightly larger in general (e.g., see the befores and afters starting here). Older screenshots will be retaken if and as time allows, but are unlikely to be updated soon; to sample thumbspage's current behavior, be sure to try the examples in the thumbspage package, and the live demos at thumbspage's hosting website.
Update:
version 2.0 later resolved viewer-page toolbar button run-together in full
by a new CSS min-width
setting, which both enabled scrolls for
very large fonts in small displays, and reserved space between buttons
by limiting the crunch. See the 2.0 note as well as
the source-code of the viewer
template file for more details.
This new dialog gives the image's date/time taken (if any), last modified date/time, file size, original dimensions, and display dimensions. Taken details reflect Exif tags embedded in the image (see the standard); original and display dimensions are via the Pillow library and JavaScript DOM, respectively; and modified and file-size data reflect the image file itself. On devices that support hovering, the cursor also changes to a pointer over the filename to make the new action more obvious.
Note that, apart from display dimensions, the info displayed by the new popup is static: it reflects the image at the time that thumbspage generated the viewer page, not the time that the image is viewed. For accurate info, be sure to rerun thumbspage if images are modified—just as you would if images are added or deleted. Also note that the new dialog is simplistic and filename taps are somewhat subtle, but both pass as usable without overly convoluting the display or its code.
Update:
1.7 later specialized the creation-date label to "Digitized" for scans
(see ahead).
More strikingly, version 2.0 replaced the info popup's former alert()
call
with a
custom dialog
that's a complete solution to alert()
's
limitations described in the 2.0 release note.
2.0 also extended the info popup's content to include a maker line
that gives device (e.g., camera or scanner) or else software (e.g., drawing program),
if either is present, and changed "Taken" to "Created" when date is unknown
to accommodate drawn images.
Update: version 2.0 replaced the former Raw button with an Auto for automatic slideshows (and later added a Full for fullscreen). Raw image display is still available via image taps—which made Raw fully redundant anyhow.
<!-- Generated
. They may be
useful in auditing or revision-tracking roles.
Tip: to strip these comments out of files to make results more comparable,
see this included
utility script.
y
(yes), instead of its former n
(no),
because viewer pages are now mature enough to broadly recommend over raw browser displays.
The default for the clean-thumbs prompt is now also y
, because this is the
normal and recommended response. These are minor usage enhancements,
but invalidate some console logs created under prior versions (two prompts differ
trivially); these examples
are being updated for the 1.7 release, but please pardon any remaining dust.
<
shell syntax to pipe in a
file of precoded replies, one per line, from a text file. To better reflect the new
argument, 1.7 also changes the order of inputs, moving file name to be asked first.
Like the prior note's change, this invalidates some older session logs (where folder
name is asked later); also like the prior note, the difference is negligible,
and examples
are being updated in the 1.7 package as they are found.
.*
hidden names in subfolders bullet lists
.
and thus
hidden per Unix convention no longer appear in subfolder bullet lists on index
pages. Formerly, folders with leading _
s were skipped as gallery
private, but hidden folders are now ignored too; use either to omit from bullet lists.
__main__
test). This change does not impact the
content or behavior of the pages that thumbspage generates, which work on
any browser as before; but it allows thumbspage itself to be run to create
galleries in contexts that support Pillow but not tkinter.
While generally helpful, this upgrade's main impetus was Python Android apps. For instance, thumbspage is now usable on Android devices in both the tkinter-less Termux, as well as the tkinter-aware Pydroid 3, after installing Pillow. Especially when using a keyboard or tablet, this makes running thumbspage on Android a reasonable goal.
For more details on running thumbspage to build galleries on Android devices, see the notebox earlier in this doc.
Summary: thumbspage 1.7 discards its former hover effects on toolbar-button mouseover, because they are supported unevenly and poorly on mobile browsers. URL popups and cursor changes still provide indicators where available (i.e., on desktops, and a renegade mobile or two).
Version 1.7 grudgingly abandons the font effects formerly applied to viewer pages' toolbar buttons on mouseover (a.k.a. cursor hovers). These effects work well as a visual indicator on devices with a mouse (e.g., desktop browsers), but are just plain buggy on devices with touchscreens (e.g., mobile browsers).
For example, hover effects commonly get stuck on after a tap on Android mobile browsers, and aren't cleared until the next user event; this is especially poor if the tap does not switch to a next page—like the new filename tap in 1.7 above. Hover effects may also require a double tap to activate the main action on iOS devices (one tap for the hover, one to activate). In thumbspage, hovering was formerly disabled for smaller screens via CSS media queries, but its problems could still crop up in landscape mode on mobile devices. Hover effects may be superfluous for toolbar buttons which also invoke a URL popup; but the filename does not, and neither do some hoverable touchscreens.
Unfortunately, the only full remedy for this today is to avoid hover effects altogether on pages that wish to support both desktop and mobile devices—including thumbspage viewer pages as of 1.7. This is an unfair penalty on desktop browsers, where hovers work correctly, and are a common and even expected behavior. But it's also typical of the quality issues that mire the mobile realm, and one of far too many interoperability issues that plague the web domain. Alas, both seem to have been more accumulated than designed to date. For more on hover problems, see the note on this MDN page, and the iOS results for this search.
As a consolation, thumbspage viewer pages still provide some visual cues when a pointing device is used, by changing the cursor to a pointer whenever it covers any widget with a clickable action (though not when the action won't fire because JavaScript is disabled). This works well on all desktop browsers tested, but has no effect on mobile browsers even when a stylus is present—except for Samsung's browser on a stylus-equipped Galaxy smartphone, a device which otherwise inhabits a strange browser purgatory between mouse and touch, and further muddles a hover story that sorely needs a happier ending.
Summary: in thumbspage 1.7, thumbnails of non-transparent GIF images have been much improved, thanks to both a fix initially installed to do the same for thumbnails of shrunken images, and the generalized utility of the underlying Pillow library.
Per the usage note above, the site hosting thumbspage has started globally downscaling image filesize with the shrinkpix program. This makes online images both faster to view, and friendlier to limited-quota users. As a consequence of the conversions, though, thumbnails generated by thumbspage from shrunken images have potential to lose visual quality; in worst cases, the effect is a nearly complete munge.
To fix this, version 1.7 now internally and temporarily converts some images to "RGBA" color mode when creating their thumbnails. This produces thumbnails that look the same and are at least as good as those made from original, unshrunk images by prior versions. As a bonus, using "RGBA" for some unshrunk GIFs renders their thumbnails much better than before; to show how, here are two pages with GIF-image thumbs before and after the change.
For more technical details on this change, see the [1.7]
notes in
viewer-thumbs.py.
This fix is not applied to JPEG images (which yield the same thumb quality
when shrunk), or GIFs with transparencies (whose transparent parts would render
black). An alternative "RGB" conversion was less constrained, but rendered
Mac OS screenshot shadows fully black, which matched shadows on
full-size viewer-page displays only coincidentally when the default black
background color was used.
These shadows may be best avoided as a rule, but this lesson comes too
late for the hundreds of captures on thumbspage's website.
Summary: as of 1.7, thumbspage propagates Exif metadata tags to auto-rotated images, and updates dimension tags for the new right-side-up orientation using a third-party library included in the package. It's important to preserve metadata in general, but this is also required for proper display and info popups.
Version 1.6 added auto-rotation of original images and their thumbnails in thumbspage galleries. With version 1.7, rotated and saved JPEG images now retain their original Exif tags (see the standard). This grew more important with the introduction of image-info dialogs in 1.7 described above. To see why this matters, click the filename on this page; this rotated image's date taken is now displayed in the info dialog, because this data comes from the Exif tags propagated from original to rotated images by 1.7.
One catch to this process: if Exif tags were propagated directly and unchanged, they would reflect the original image, not the rotated copy. Among the possible consequences, width/height dimension tags wouldn't reflect the new swapped dimensions of the rotation. This isn't crucial, because rotated images are meant for use in thumbspage galleries only, and prior values document originally recorded size (though they may confuse other tools).
The orientation tag, however, cannot be propagated to saved rotations unchanged; if it's not updated to the normal-rotation value, other viewers will incorrectly reorient already-reoriented images, resulting in flipped displays. This happens even in the underlying web browsers that display such images directly in thumbspage's own Raw mode (a.k.a. image tap). Perhaps worse, if the orientation tag isn't adjusted, thumbspage itself will reorient the image and its thumbnail on every new gallery-build run—with erroneous, if comical, results.
To fix this, thumbspage updates both orientation and dimension values in the Exif data while propagating it from original to rotation. This both saves date taken, and rights the Raw apes. To perform these updates, thumbspage now uses the third-party piexif library, and ships this library's code in its download package. The much broader Pillow image-processing library used everywhere else in thumbspage must already be separately installed, but has almost no support for Exif tags, apart from fetching and saving their raw bytes data. By contrast, the pure-Python piexif both parses and composes Exif data, and provides read/write access to individual tags.
All of which is substantial added complexity. Without Exif-tag propagation, though, date-taken would not be displayed for rotated images alone; without Exif-tag updates, rotated images would be flipped by browsers and reoriented on each thumbspage run. The former would probably qualify as a bug, and the latter would be worse.
Also note that Exif propagation is currently applied only to JPEG images. PNGs have some notion of Exifs too, but it's just recently been standardized, was largely ad-hoc in the past, and the priority here is on commonly available photo data that thumbspage galleries and their info popups display. If present at all, PNG tags seem unlikely to be as widely useful as the information slavishly stamped onto JPEG photos by our cameras and smartphones.
Update: see also the June 2020 work-around ahead, applied after piexif was seen to fail on some uncommon Exif tags; updating tags makes thumbspage only as robust as a third-party library.
Update: version 2.0 explored the idea of dropping rotations in thumbspage itself, by propagating sources' Exif orientation tags to their thumbnails and relying on browsers to rotate. This was abandoned because browsers don't support rotations universally; thumbspage must rotate because some browsers don't. See the 2.0 note here.
-webkit-text-size-adjust
style in default thumbnail-index pages, to disable iOS Safari's upscaling
(a.k.a. boosting) of some text sizes in landscape orientation. Upscaling is
arguably an iOS Safari misfeature: the zoom prevents users from seeing more
text in the wider format after rotating to landscape. The style—arguably
a hack, but common on the web—is new in index-page default headers only;
it was already used in generated image-viewer pages,
and added manually by most galleries with custom index headers. It's generally
recommended, but you can restore prior behavior by either changing the new
noiOSIndexTextBoost
setting in the configs
file, or using a custom HEADER.html
file sans the new hack.
Summary: in popup info dialogs triggered on filename taps, thumbspage 1.7 now labels capture dates for scanned images as "Digitized" instead of "Taken," as in this. This avoids potential confusion, but relies on device manufacturers (and programming libraries) to use Exif tags consistently.
The Exif standard's use of
multiple date/time fields leaves roles somewhat vague, and relies on consistent
interpretation by device manufacturers.
In particular, the two main date/time tags—DateTimeOriginal
and DateTimeDigitized
—seem prone to interoperability skew.
In practice, though, images captured on digital cameras normally have
both their original and digitized tags set to capture
(i.e., taken) date/time. While comparatively rare, scanned images
instead generally have a blank original tag, and record scan
date/time in the digitized tag.
Exif defines an additional DateTime
tag, but it's meant
only for recording time changed, and is considered out of scope by thumbspage
(and likely redundant with the file's modification time already shown).
In prior 1.7 releases, thumbspage's info popups used the original tag if present, else the digitized tag, and labeled the result "Taken" for either. This logic was borrowed from tagpix, which must select a filename prefix for uniqueness. It makes sense in thumbspage too if "Taken" is generalized to mean any digital origin, but might be mildly confusing for photo scans: the scan date/time doesn't reflect that of the actual scene, and most people are unlikely to manually add an Exif capture-date/time tag to older scanned images (when known at all). Here's how the prior release potentially puzzling display for a scan.
To do better, thumbspage's info screens now display either a "Taken" date/time, for images with an original-tag value; a "Digitized" date/time, for images with only a digitized-tag value; or "Taken: (unknown)" as before if neither tag is set (this is the normal case in thumbspage for PNG screenshots). Here's the new, improved results for a camera photo, a scanned photo, and a screenshot. This should better reflect photo scans in galleries—especially for photos with years in their filenames, like some at this site. This also accommodates screen-size limits in the info popup: displaying all date-related tags might run off screen on small mobiles, and date of origin is usually the main item of interest.
Beyond all this, it's also worth noting that Exif date/times generally have no time-zone information (a new standard does, but it's not widely followed); they reflect the recording device's time, which is presumably that of the capture location. Moreover, metadata can appear in multiple redundant-but-differing formats in digital images, and thumbspage is wholly dependent on the Pillow library to collect it, and devices to supply it. Although results have been good to date, digital image metadata, like much in the computing world, seems to have been as much accumulated as designed. For more on Exif issues, try this.
Summary:
as of 1.7, thumbspage now also uses JavaScript scaling for all iOS browsers in landscape
mode—including Safari.
This works well in all four non-Safari iOS browsers
tested,
and matches their Android
behavior.
It's also arguably better in Safari on iOS < 13: users must still scroll
once,
but it's easier to view the bottom of an
image
than
before.
Best of all, the new scheme works
ideally
as of iOS 13 in Safari, if the hide-toolbars option is enabled in its new
"aA"
menu.
As a legacy option, the former 1.5 CSS scaling can still be used
for iOS Safari's landscape
views
where preferred; see the configs file's
iOSSafariLandscapeCSS
.
Per this guide's pre-1.7 coverage here and here, thumbspage 1.6 reverted to 1.5's CSS-based scrolled scaling for landscape (i.e., vertical) orientation display on iOS devices. This was an intentional compromise, because page sizes were returned inconsistently in this context; no good work-around could be found for usability issues; and iOS's low traffic share at this technically focused website didn't merit additional efforts at the time. iOS's overall share was 3%, and the most popular alternative to Safari on iOS weighed in at just 0.4% (and lower if analytics blockers were factored in; details here). When time is limited, it's tough to justify investing it on outliers, especially when the only hope seems brittle kludges.
In retrospect, this policy may have been too broad. For one thing, not all sites' iOS percentages will be as low as those here. For another, most iOS issues were present only in Safari: only iOS Safari reported viewport sizes poorly, and only iOS Safari cropped landscape pages with mandatory toolbars. While Safari likely accounts for most iOS traffic on the web, 1.6's blanket policy unfairly penalized other iOS browsers whose landscape displays work well. The real clincher here, though, was a new option in iOS 13 which works around Safari's issues in full, and easily justifies a new approach.
To do better, thumbspage 1.7 by default now uses 1.6's JavaScript scaling for landscape views in all browsers on iOS, instead of the falling back on the former CSS-based scaling in this context. This is a clear win for all non-Safari iOS browsers tested; here are the results on a 4" test-device screen for Chrome, Firefox, the latest Edge, and UC Browser. These displays now better match those on Android, which has always scaled well in both orientations.
For iOS Safari, results are improved across the board, but vary by iOS release and user action. The best news is that thumbspage landscape views now work in full for users running iOS 13 and later. As of that version, iOS Safari has gained an option that hides its bottom toolbar altogether, and shrinks its top toolbar substantially. This option lives in the new "aA" menu at the upper left of Safari's window, and is applied to all pages opened in an enabling tab. For more usage details we'll skip here, try a web search; iOS 13 may also hide toolbars automatically when rotated to landscape, but the menu setting makes the effect permanent, and provides additional UI fixes. Specifically, when the new iOS 13 option is enabled:
All that being said, the thing about options is that they're optional. Although the new iOS 13 work-around can indeed solve multiple thumbspage issues in full, it's important to keep in mind that it won't help users who don't know about the new option; choose not to apply the new option; or use devices running iOS 12 and earlier that don't have the new option (yes, it's still possible). That's far short of an all-inclusive fix. Where the iOS 13 option isn't available or enabled, the new JavaScript scaling results in 1.7 are still obscured by toolbars and require a scroll, but they at least allow users to view image top and bottom more easily (the former CSS display revealed image bottom only during a downscroll).
Browser flux aside, thumbspage's new scaling rules are very simple: it now uses JavaScript
image scaling everywhere, with just two exceptions. First, CSS-based scaling
is still used as a fallback when JavaScript is
disabled.
Second, because this change
differs from prior releases, and because some users may very well prefer the former
results in Safaris not using the iOS 13 work-around, you can reinstate CSS-based landscape
displays
by setting the iOSSafariLandscapeCSS
variable in the
configs file. This legacy option works for iOS Safari
only, because all other browsers handle JavaScript scaling without issue.
To summarize, thumbspage's complete image-page user story on mobile is now as follows:
At least that's the story until the next browser change. If web browsers are a kind of GUI, then web development is like having to make your code run on dozens of incompatible versions of a GUI toolkit, and on dozens of disparate platforms, all of which morph regularly and frequently and are naturally inclined to view interoperability as an obstacle to control. This has wrought a programming and maintenance nightmare—and ironically so, given the web's original portability ambitions. Please enjoy thumbspage today, but bear in mind that longevity cannot be counted among the assets of web-based systems.
Footnote: to be fair, iOS Safari isn't alone in harboring usability "features" that break websites. Edge for years had a URL-mouseover defect which rendered toolbar buttons unusable at the bottom left of a desktop page; and Chrome's duet adventures on Android regularly threaten to steal a sizable chunk of the display for browser agendas. Edge's overlay was fixed, and Chrome's duet is currently not a default, but both are typical of the sorts of vendor quirks that developers must battle constantly. A decent conspiracy theorist might point out here that vendors have vested interests in driving content to fullscreen web apps which generate store revenue. More likely, these are just artifacts of the opinion-based morph that plagues software at large, and renders web development in particular as much hacking as engineering. Improve me...
Summary: The piexif third-party library raises build-time exceptions for some uncommon Exif tag types with values coded unexpectedly by some cameras. thumbspage 1.7 now tries to correct specific known instances, and skips auto-rotation for others with a message instead of terminating the run; manually rotate the offenders and rerun if desired. This patch doesn't change the code of any generated pages, so no existing galleries need be rebuilt for its point release.
As described earlier, thumbspage 1.7 adopts and bundles the piexif third-party library, for updating Exif orientation and dimension tags in auto-rotated images. The Pillow library used by thumbspage for all other image processing can extract and set Exif data as a single blob of bytes, but cannot readily change individual tags; piexif adds parsing to and generation from an easily changed tags dictionary.
Unfortunately, piexif also appears to have a bug which causes it to raise exceptions at gallery-build time for some less common Exif tags, when trying to convert its changeable dictionary back into a storable blob. This happens even for tags parsed by piexif itself and unchanged by thumbspage. Whether or not the offending data uses type coding that deviates from the Exif standard, this seems a clear design flaw: piexif should be more forgiving, and should not abort when reformatting data which it created.
For a general review of the issue, see the formal report of this piexif bug on GitHub. In thumbspage specifically, the following cropped up for photos shot on a Samsung Galaxy Note 9 smartphone which were part of a folder being built into a gallery:
Making thumbnail: test/_thumbspage/2018-11-15__172646.jpg --Reorienting tilted image --Source rotate failed: skipped Exception: "dump" got wrong type of exif value. 41729 in Exif IFD. Got as <class 'int'>.
This photo was shot in 2018 and is hardly ancient in 2020, but Galaxy cameras have historically had issues conforming to the Exif tag standard (a different tags-related issue arose in the tagpix program for photos from the same device category). Still, piexif should do better than failing with the message above. In this case, tag 41729 maps to SceneType—an uncommon Exif tag that piexif correctly defines as a byte but happily parses as an integer, which in turn triggers exceptions in its unparsing method.
Also unfortunately, although thumbspage did catch and recover from the piexif exception, it failed later when querying image size, because the offending image file was not present—its original was moved to its backup copy, but never rewritten in rotated form. This terminated the thumbspage run in full, and left the gallery unbuilt:
Generating view page for: 2018-11-15__172303.jpg Traceback (most recent call last): ...details cut... FileNotFoundError: [Errno 2] No such file or directory: 'test/2018-11-15__172646.jpg'
To work around this, thumbspage now both fixes known offending tags, and restores originals on failing rotations so builds can proceed. For example, thumbspage special-cases tag 41729 to accommodate piexif's expectations, with code like this:
origexif = imgstart.info.get('exif', b'') # original raw bytes from Pillow parsedexif = piexif.load(origexif) # parse to dict for changes here ... parsedExif = parsedexif['Exif'] # parsed tags: dict of dicts if 41729 in parsedExif: tagval = parsedExif[41729] # miscoded on some Galaxy if type(tagval) is int: # munge from int to byte if 0 <= tagval <= 255: parsedExif[41729] = bytes([tagval]) print('--Note: bad SceneType Exif tag type was corrected') else: del parsedExif[41729] print('--Note: bad SceneType Exif tag type was dropped') ... saveexif = piexif.dump(parsedexif) # back to raw bytes for Pillow
Per another piexif bug report, a similar fix is applied for tag 37121 to convert integer tuples to bytes, though this was never seen to crash thumbspage. When corrected, tags leave a note in the run's output:
Making thumbnail: test/_thumbspage/2018-11-15__172646.jpg --Reorienting tilted image --Note: bad SceneType Exif tag type was corrected
Additionally, on piexif failures, thumbspage now automatically restores the failing image's backed-up original, to avoid terminating altogether. The failing image won't be rotated, but the gallery build will run to completion successfully, and a message in the logs directs users to try rotating the offending image manually and rerunning; here's the scene if the 41729-fixer code is disabled:
Making thumbnail: test/_thumbspage/2018-11-15__172646.jpg --Reorienting tilted image --Source rotate failed: skipped Exception: "dump" got wrong type of exif value. 41729 in Exif IFD. Got as. --Rotate manually and rerun if desired ...and finish the build...
To see all the code changed by this update, search for Jun-2020
in
viewer_thumbs.py. Again, no generated
page code is modified by this patch; it's an improvement to
build-time mechanics only. thumbspage also considered rotating without
Exif updates on piexif failures (as before 1.7), but the consequences for
this are worse than requiring manual rotations for skips.
As a reminder, you can always disable image rotations altogether in the configs file,
if the failure messages grow too grievous; see the 1.6 note.
Like many, this work-around is a stopgap measure. The real solution is a fix for piexif which better handles oddball tags, and type mismatches in general. As is, this seems a general and broad issue in the library, which may not be addressable by one-off tag patches in clients. Until a better piexif becomes available, the best thumbspage can sometimes do is skip auto-rotations for photos that crash piexif's otherwise highly useful utilities. Alas, third-party dependencies make programs, well, dependent.
Version 1.6 was finalized on October 28, 2018. This version was released multiple times with new features in each release; release dates identify point releases. Its main extensions are dynamic image scaling and image auto-rotation, but it introduces numerous enhancements to the program—including this HTML guide.
This version was last repackaged as an interim release in February 2019 with a minor change to avoid a tkinter dependency for contexts that have Pillow but not tkinter (e.g., some Android Python apps). The tkinter module is now required only when running the simple GUI viewer. For more details, see the update note above for version 1.7, which formally adopted this change.
Despite the prior version's hand-waving, thumbspage's image-viewer pages now use JavaScript in their generated HTML to dynamically scale images to available display size, while preserving original aspect ratio and avoiding content screen overflow. The net effect emulates browser-native scaling in viewer pages, while allowing for extra control widgets. The former CSS scaling scheme is still used, but only for no-JavaScript views and iOS landscape mode. In more detail:
Exception: on iOS only, images are always fully scaled in portrait device orientation, but for implementation reasons fall back on version 1.5's CSS scrollable scaling result in landscape device mode (in short, display size returned by iOS in this mode is unusable). Android fully scales images always, which yields smaller but complete landscape images—which is arguably better, but open to feedback.
Update: thumbspage 1.7 now also uses JavaScript scaling for images in landscape mode on iOS, in all browsers. This includes iOS Safari, which still has landscape issues in earlier versions, but gained a new toolbar-hiding option in iOS 13 which can fully solve former thumbspage problems. Chrome, among others, now works the same in landscape on Android and iOS. Read more about this change here.
On both device types, a "Loading..." message is also posted as a visual status indicator. Version 1.5 deemed JavaScript a "nonstarter" partly because of the added complexity, but mostly because locking out users who don't wish to run JavaScript is a rude non-option. Here, though, pages still work when JavaScript is disabled—they use the former 1.5 CSS scaling, with a note recommending JavaScript results. Turn off JS in your browser to see how 1.5 CSS scaling compares, and see the code for more details.
thumbspage now automatically reorients (rotates) tilted source images and their thumbspage-generated thumbnails to be right-side up (top edge on top), if they have valid "Orientation" Exif tags (see the standard). This is really just an automatic alternative to manually rotating tilted images before making thumbs, but is especially useful for photos shot on smartphones that commonly tilt photos shot in the natural portrait (vertical) device orientation.
Less positively, this feature applies only to images with reliable Exif tags (e.g., JPEG and TIFF) from cameras and tools that tag as expected. More intrusively, this feature must rotate source-image files too (not just their thumbnails), because not all viewers will rotate images when opened from thumbnails. In thumbspage specifically, web browsers will not rotate tilted source images (except on "Raw" clicks), because its viewer pages display source images as in-page elements. The related PyPhoto program now also adjusts for orientation in memory only and does not require image copies, but uses forked thumbnail-generation code (see its website).
Because this feature modifies source images in-place, it by default saves
them to backup copies with a .original
extension before making
changes. Users may also control the feature with two new settings in
user_configs.py
:
autoRotateImages
can turn the feature off,
and backupRotatedImages
can skip its .original
backups. Disable rotation if needed or desired, and manually rotate
tilted images before running thumbspage as preferred.
See examples/reorientation
for a test case's results; that folder's
restore-originals.py
utility to restore from backups; and module
viewer_thumbs.py
for implementation code
(rotation is neither an automatic nor an easy option for Pillow/PIL thumbnails).
Caveat: rotated source images drop the originals' Exif tags,
but rotated images are not the same as the original (some tags might not apply);
they are meant to be viewed in HTML galleries only; and some other image-processing
tools drop the tags too.
Update: version 1.7 now retains and updates Exif tags in JPEG images rotated and saved (read the details). This Exifs propagation grew more important with 1.7's new info dialogs which display date-taken tag data.
Update: though rare, it's possible for rotations to fail due to permission errors, miscoded Exif tags, and other reasons. Check the output for messages if a photo remains tilted in a gallery, and see this 1.7 note for one cause of rotation failures.
Update: as noted at version 1.7, 2.0 explored the idea of dropping rotations in thumbspage itself, by propagating sources' Exif orientation tags to their thumbnails and relying on browsers to rotate. This was abandoned because browsers don't support rotations universally; thumbspage must rotate because some browsers don't. See the 2.0 note here.
In addition to the main items above, version 1.6 also:
Adds the HTML user/developer guide you are reading, as a replacement for the former in-code and text-based documentation. Text is easier to code, but HTML can be much nicer to read.
Moves configuration options and viewer-page HTML to separate files for easier viewing and edits. See Customization above for links and story.
Uses _thumbspage
as the default name of its thumbnails +
viewer-pages subfolder, to avoid clashing with other content.
The former "thumbs" name default may still appear in some docs and screenshots.
Prior-Version users: your "thumbs" folder will be unused; manually
delete or rename, or set THUMBS='thumbs'
in
user_configs.py
.
Expands image-folder name .
to its true basename in generated
default-header text, and appends a /
to subfolder hyperlinks
so they will not trigger redirects or clash with files if and where
it matters. Because the .
fix uses Python's os.path.abspath()
to expand the dot, it also properly names image-folder paths with any "."
or ".." (e.g., ".", "..", "../..", "../Desktop/trnpix/../trnpix/.", and other oddities).
Adds
a new configuration setting, templateEncoding
, which
allows the Unicode encoding of the viewer page template file to be easily configured,
and differ from that of index-page header and footer files. Its preset default and
generally recommended setting is the broad UTF-8;
edit user_configs.py
to tailor.
Note that this new setting is used for loading the template file only;
generated viewer pages instead use the same Unicode setting for saves as
index pages—outputEncoding
. The two settings may be the same or
differ, depending on your usage. The Unicode encoding declared in viewer-page
<meta>
tags also uses outputEncoding
automatically so that it
agrees with page content; if you manually edit this tag, it should similarly agree.
Update:
as of version 2.0, templateEncoding
is now also used to load the
floating Top button's template-floatingtop.html
file, whose code
is added to index pages per outputEncoding
. See the 2.0
note.
Adds an expandSmallImages
option in
user_configs.py
.
If False, the maximum viewer-page scale ratio is 1.0, which
constrains images to an actual-size maximum, and thereby
avoids stretching and possibly blurring small images. If
True, smaller images are always expanded to display size.
The preset default is False, because this is generally better when smaller images are present (e.g., icons, and small-window screenshots). Use True for the prior expanding behavior which may be preferred in some contexts. Note that this setting applies only to small images; most digital photos and scans are far larger than display areas, and will only be scaled down.
Uses JavaScript viewer-page code to avoid adding viewer pages to destack browser history where supported, so that N image views don't require N browser Back clicks to get back to pre-gallery context; a single Back returns to the gallery entry page. This destacking works everywhere but Chrome on iOS, where the feature is disabled (i.e., viewer pages are stacked on history and retraced by Back clicks for this browser only). The JS template file hosts most of the implementation's code (view its source).
This feature works and is used in Chrome on Android+Windows+Mac;
in Safari+Firefox on iOS; and in all other 20+ browsers tested
(including Internet Explorer 9, and other Android browsers). The failure
in Chrome on iOS is clearly a bug in its location.replace()
of versions 64 and 68 tested; if used in this browser, Back clicks
redisplay the same page N times for N image views, which is
worse than stacking pages.
Because this might be fixed in the future,
user_configs.py
's new
chromeiOSBackFixed
controls the iOS Chrome disabling.
At present, though, Chrome on iOS is just 0.40% of the audience at the
site hosting this program (just 10.96% of iOS, which is itself just 3.67%
overall); it makes no sense to omit an enhancement for 99.6% of users,
for the sake of just 0.40%. For more of this site's analytics,
visit this page.
Update: history destacking is still broken in
iOS Chrome, as of its version 75 in May 2020. Its history-cropping
location.replace()
method runs without error, but silently
stacks all pages. This requires Back clicks to retrace all images
viewed—marginally better than displaying the same page N times,
but still nonfunctional. The disable is still on by default in the
configs file, though its effect is currently moot (pages are stacked either way).
Update:
because this iOS Chrome work-around is no different than the browser's
current buggy behavior, version 2.0 makes the work-around bypass switch's default
True
. This skips the work-around, and stacks navigation pages
in this browser's history today, but will also pick up a true Chrome fix if
one ever appears.
Update: as of June 2020, this iOS Chrome history bug appears to now be fixed in Chrome 83 (its actual fix version is unknown, but Chrome 75 was still broken). No changes are required, because the former update's config-file change sufficed to adopt the new, fixed behavior. For more details see the 2.0 note.
Works around a Pillow library bug that could occur only in limited usage contexts for folders having very many images. In brief, Pillow's auto-close of loaded image files does not work as documented, which can lead to "Too many open files" errors after many thumbnails have been generated. Here, this meant that results could reflect partial source content for very large folders, though only on Mac OS in general.
The best and applied fix is to manually open and close image files,
instead of passing filenames to Pillow. With this change,
arbitrarily large folders are supported in all contexts.
For more details on both the bug and its work-around, see
module viewer_thumbs.py
,
where the fix is coded.
Works
around a Chrome issue on Windows and Linux, by using
auto-scroll: hidden
CSS for the body, to forcibly
hide the vertical scrollbar. Else, Chrome (and possibly
older Firefox) flash a scrollbar momentarily during viewer-page
loads. This setting doesn't impact displays in any other
way, and scrollbars are never required on viewer pages (images
are scaled, not scrolled).
Caveat: Chrome on Android (only) may still sometimes very briefly flash a vertical scrollbar anyhow. This may be an indicator or other normal behavior, but seems more likely a browser bug with no known work-around—applying the hidden setting to "html" in CSS doesn't help. This is minor and cosmetic, but like much web experience, has to be chalked up to browser idiosyncrasy.
Works
around a Chrome desktop peculiarity, by using
thin
instead of 1px
for CSS <img>
border-width in both index
and viewer pages (for thumbs and images). On this browser only,
1px
can cause some image borders to not be
drawn at zoom levels < 100% due to fractional pixel math.
thin
is equivalent to 1px
in size today (not
2px
, as once rumored on the web), but does not suffer from
pixel-math cloaking, and doesn't impact displays otherwise.
This works through Chrome desktop zoom level 50%; below that
Chrome (again, only) may drop viewer-page bottom borders for some
window sizes, but that's a reasonable usage cutoff (pages requiring
a microscope are off-table). Using a 1.5px
almost works,
but can add empty space between border and content. To test borders, see
this site.
As part of this work-around, image border color was also made
configurable for both page types; set border color to background color
to omit borders altogether. Note that index-page table top/bottom borders work
unchanged as 1px
, and this 1.6 change is disjoint from 1.5's
<hr>
Chrome fix noted ahead.
Version 1.5 was finalized on August 12, 2018. This version's main feature is the introduction of image viewer pages, which were further improved in version 1.6 (see its notes). A set of extra enhancements rounds out the release.
In addition to its former thumbnail-index pages, thumbspage now generates a styled viewer page for each image in the folder, instead of relying on each browser's native image display.
Viewer pages are opened on index-page thumbnail clicks, and have filename, the image scaled per CSS, view-native and go-to-index links, and previous/next-image links that cycle through all images without having to return to the index page. Viewer pages center images horizontally, but not vertically; the latter is too jarring during next/previous navigation.
Viewer pages are generated in the thumbs folder, along with the prior version's thumbnail-index image files. They can also be suppressed via console input prompts; when omitted, images open in browsers directly and natively, as before. To view an example client live, visit this site.
Caveat: because image scaling is weak with CSS alone (and JavaScript seems both overkill and non-starter for this project), 1.5 image-viewer pages should be considered an optional feature. See "1.5 Usage Note" for deployment suggestions, and in-code documentation in the main script for more details.
Update: version 1.6 later replaced 1.5's CSS scaling with much better JavaScript dynamic image scaling for most use cases, and deleted the now-moot "1.5 Usage Note" referenced above. See the 1.6's release notes above.
In addition to the main items above, version 1.5 also:
Formalizes index-page and navigation ordering. It's now
case sensitive by default everywhere, but can be changed in
user_configs.py
if case-neutral (Windows-like) ordering is preferred;
see that file's setting caseSensOrder
.
See also orderedListing()
in
thumbspage.py
for more details.
Formalizes
URL-escapes' Unicode encoding, which determines the content
of %xx
-formatted bytes. It's now always UTF-8,
regardless of the encoding used for whole HTML pages, because this
seems to be required by both standards and browsers.
See also url_escape()
in
thumbspage.py
for more details.
Sets body font to Arial (sans serif) for default-header index
pages. This is cosmetically nicer, and matches the new viewer pages' precoded font.
This font is set for the body in a default header <style>
block only and not
inlined in generated page components, so that global font can differ in a custom
HEADER.html
file.
Works around a desktop Chrome <hr>
bug
which botches the separator line (it's much lighter at some zoom levels than
others). To fix, the thumbs table was restyled to use table top and bottom
borders instead of <hr>
s. As a consequence, the thumbs table now
always stretches to 100% window width, to extend the border
lines (this was formerly a configuration,
off by default). See also the 1.6 Chrome fix for vanishing
<img>
borders.
Sets thumbs-table background color to light grey as part of
the former note's <hr>
restyling, and allows it to be changed
in user_configs.py
.
The new viewer pages' colors (and others) can be similarly tailored
in that file.
Refactors its code to functions (it's now large enough that top-level code is difficult to navigate), and cleans up its page output (HTML/CSS is tough to read as it is).
Still uses all-inline styles for the thumbnails tables on index pages,
not <style>
blocks, so that custom
HEADER.html
files
need not include or link to styles for the generated table. Conversely,
viewer pages use a <style>
block, as they are not customizable
without HTML edits (yet?).
Version 1.4 was finalized on March 4, 2018. In this release, this script's output page better supports browsers on smaller screens (e.g., mobile phones and tablets), and looks nicer in general. Its new generated CSS code:
Autoscrolls the thumbs table to appease mobile browsers.
Adds padding to thumb cells to reduce run-together.
Center-aligns thumbs images for a more even look; this helps overall, but especially for narrow/portrait images.
Uses nowrap paragraphs for image labels; the former
<br>
+wrap scheme looked jumbled, and made Chrome (only!)
arrange thumbs-table columns unevenly in small windows
(the leftmost was narrower).
This version also adds a mobile-friendly viewport <meta> tag to
default headers if useViewPort
is True in
user_configs.py
(this is its
preset default). This may impact other page components; use a custom
HEADER.html
file for more
options and control where needed.
Tip: because filenames used on labels are now not wrapped, their width largely determines column spacing in the index page; use shorter filenames for less space between (i.e., narrower) columns.
Version 1.3 was finalized on August 8, 2016. This version makes several changes to better accommodate arbitrary non-ASCII Unicode filenames and page content (see the live demo). Specifically, this release:
HTML-escapes all added text—image, folder, subfolder names.
URL-escapes all added links—to thumbs, images, subfolders, and viewer pages in 1.5.
Outputs the generated index file in UTF-8 Unicode encoding by
default, with a content-type <meta>
tag. ASCII content is
unchanged, as it is a subset of UTF-8. Other encodings may be used for
the output file via setting outputEncoding
in
file user_configs.py
.
This setting is also used to save viewer pages, per the 1.5 update below.
Loads any header and footer inserts per UTF-8 Unicode encoding
by default, as it is general and supports ASCII directly.
Other encodings, including the platform's default, may be
used for inserts via setting insertEncoding
in
file user_configs.py
.
Note that this setting is used for insert-file loads only; generated
pages are always saved per outputEncoding
, which may or
may not differ in your usage.
Assumes any inserts are both HTML-safe and compatible with the default or configured inserts encoding. Be sure to verify your inserts before using this program's results.
See examples/unicode/images for the results of a comprehensive Unicode-content test case.
Note: if you use a custom
HEADER.html
file,
make sure that the Unicode type declared in its content-type <meta>
tag matches the setting for outputEncoding
(#3 above).
thumbspage uses the latter to save the whole index-page (including the content
of its thumbnails table), so these two encodings must be the same or compatible.
Update:
version 1.5 further refines URL escapes to use UTF-8 encoding for escape
byte values,
and loads its new viewer pages as UTF-8 by default
(configurable as of 1.6 by setting
templateEncoding
;
viewer pages are still output per outputEncoding
).
The Unicode test's folder also moved to the new path named above, and
thumbspage development switched from Windows ("\") to Mac OS ("/")
along the way, though some docs may still retain their former Windows bias.
Version 1.2 was finalized on August 1, 2016.
This version adds assorted cosmetic tweaks, mainly in the name of
better thumbnails-table styling, and subject to settings now in
file user_configs.py
.
Primarily, this release:
Uses uniform-width columns, instead of sizing columns
per content (on by default; see setting uniformColumns
).
Stretches the thumbs table to fill the whole window or display
(off by default; see setting spanFullWindow
).
Update: this is now always on to
expand the table borders added for the 1.5 <hr>
fix.
Adds a scrollbar if the window is too small? (prototyped but skipped in 1.2).
Update: this was obsoleted by version 1.4's auto-scrolls.
Version 1.1 was finalized on July 27, 2016.
This version adds an automatic
subfolder-links list
to the index page just before the thumbnails table, if enabled by
setting listSubfolders
in file
user_configs.py
.
This is on by default, and skips folders whose names are prefixed
with a _
or .
character, as well as the generated
thumbnails folder (whether it's named with a _
or .
prefix or not).
Note: if enabled, the subfolder-links list is generated whether you
use a custom HEADER.html
file or
not. When a custom header is used, the list appears after the header's text.
Hence, pages that use a custom header may wish to either accommodate the list
in the header's opening content, or disable this feature and code the list
manually as desired. Naturally, this may vary per gallery, and matters only
if there are subfolders in your images folder.
Tip: thumbspage must be run on each image subfolder in a tree separately (the tree is not walked automatically because its folders may have arbitrary content); and when viewing offline, index pages in subfolders must be clicked manually (online viewing normally opens image subfolder indexes automatically).
Version 1.0 was finalized on July 24, 2016. It provides a basic thumbnails-index page, with generated thumbnail images that open browser-native views. Its functionality is limited, but its results already beat default folder pages.
As normal, later thumbspage releases were spurred by using this program over time. Where it goes next is limited only by developer availability and user experience. The next and final section sketches a few TBDs for future consideration—and argues against many of them.
Like all software, thumbspage is an open-ended project.
In closing, this section collects implementation-related issues
and proposals, some of which hail from early releases and have
been resolved over time.
They're likely of most interest to developers, though some usage
context might also be gleaned along the way.
A few of these items are also outstanding questions, open to feedback;
if you have preferences or suggestions, please pass them along with the
Input
link on this doc's bottom toolbar.
Also note that this list is not necessarily complete; search for "caveat" in code files for additional code-related items, and see Version History above for more on changes both noted and not noted here. This section hosts the following content:
Less positively, thumbspage also does not update an embedded thumbnail when it rotates a full image (it rotates only the thumbnail it generates itself). This, however, seems a trivial concern: thumbspage gallery images are largely intended for browser-based display, and other tools should continue to automatically accommodate any lingering thumbnail orientation.
Update: 1.7 now retains and updates Exif tags in JPEG images rotated and saved (read the details). This grew more important with 1.7's new info dialogs which display date-taken tag data.
Update: 1.7 shows Android landscape images slightly larger after a fix for large-font image clipping, and makes it more natural to view images full size by making an image tap the same as the Raw button (see the details here). This helps, but the Android landscape scaling question is still open.
Update: 1.7's final release adopted JavaScript scaling for images in landscape mode on iOS, in all browsers. This includes Safari, which still has issues in older versions, but gained a toolbar-hiding option in iOS 13 which can solve its problems, and justifies the new uniform approach. Chrome and others now work the same in landscape on Android and iOS. Read more about this change here. CSS scaling is still available for Safari on iOS, but only as a legacy option.
location.replace()
bug that required
disabling a Back feature on that browser, per version 1.6 notes (and their
May 2020 update) above. Watch for a browser
fix and change the configuration if and when it appears.
Update: Per the 1,6 note above, iOS Chrome fixed this bug as of version 83 (and perhaps earlier), and the thumbspage config file setting was changed in 2.0 to skip the temporary work-around; unless this resurfaces in the future, no other action is required.
user_configs.py
, and custom index-page fonts can be
had via CSS in a HEADER.html
(see the configurations file for pointers).
Still, user customization is open
ended—and pending usage feedback. For example, default-header
title and text could be configurable too, though they naturally
vary per folder and might have to be changed often.
Update: 1.7 added an image-info
dialog, but kept the program simple by
using a JavaScript alert()
call, and avoided GUI clutter by invoking the
dialog with a tap on the existing filename field. This dialog's information
is mostly build-time static, and could be more grandiose, but it probably shouldn't be.
Update: 2.0 expanded info-popup content, and replaced
its alert()
call with a custom dialog (a page overlay with opacity
and scrollable content box). This is more complex, but is a much better solution,
given the limitations of alert()
described in the 2.0
release note.
Update: 2.0 implements a fullscreen mode toggled by a new Full button, but makes it optional because of its downsides. Besides being unsupported on some platforms, JavaScript-induced fullscreen lasts for just one page, which makes it much less useful than manual but persistent fullscreen options available in many browsers. Hence, Full is shown by default but can be easily disabled per gallery if it seems just a browser parlor trick; your gallery's users may, however, find it useful as a quick zoom. If used, Full doesn't crowd or break viewer toolbars because they now scroll (but normally don't have to, even on small devices). For more details, see the 2.0 release note.
Implementation notes:
thumbspage's fullscreen toggle uses the JavaScript requestFullscreen()
call in document.documentElement
, along with its cohorts. Because this API
is not universally
available,
prefixed variants are also tried on browsers that don't support the standard; even then,
some browsers ignore fullscreen requests altogether today. Hence, thumbspage's Full isn't
supported where it isn't supported. Such is life in the splintered world of web development.
Update: 2.0 added automatic slideshows and redesigned viewer-page toolbars to accommodate them: instead of adding widgets, slideshows are toggled by a new Auto button that replaces the former Raw, which grew redundant with 1.7's addition of filename taps. The slideshow's button is a no-op without JavaScript, but so are dynamic image scaling, info popups, and Top; users are warned if JavaScript is off, and thumbspage now generally assumes that it will be on. The toolbar now also scrolls to alleviate UI cramping altogether, though this is more important for the preceding note's Full. For more details, see the related 2.0 notes here and here.
Implementation notes:
cross-page state retention in JavaScript turned out to be
trivial: HTML5 session storage, client-side cookies, and the window.name
attribute all persist between pages, and can be used to record a slideshow toggle.
Of these, the latter was used initially because it's simple and won't be disabled,
but HTML5 session storage was later adopted due to a flaw in Safari: see above.
Note that other JavaScript variables won't work—unlike the DOM-defined
window.name
, page-defined attributes (e.g., window.autoToggle
)
do not persist across pages; if used, Auto switches pages once, but the attribute is
undefined
in the next page.
Handling JavaScript timer events is also easy: they're cleared manually
by the page on toggle off and automatically by browsers on gallery/page exit,
and are rescheduled on each viewer-page load while the tab-global slideshow toggle is on
(some of which may be more conceptual than literal: browser back-forward caches and
quirks may muddle this story).
Subtle to be sure, but broadly functional nonetheless.
Update: 2.0 added a floating Top to index pages as an optional-but-default widget, because some pages do have useful top content, users may wish to start a slideshow at the first image, and large indexes can make for lots of scrolling on mobiles. Still, Top can be omitted via a setting in the configs file if it seems useless clutter for your gallery's use case. Where used, its colors, scroll threshold, and page location can also be tailored for custom needs (e.g., for larger or smaller headers and toolbars). For more details, see the 2.0 release note.
Update: no, it cannot—per the 2.0 note, browsers do not support rotations widely enough in fall 2020 to rely on this. thumbspage must rotate images itself because some browser's don't.
This hasn't been pursued, in part because it seems overkill: thumbspage is a utility that likely won't be run on a frequent basis, and its target audience is not technically naive—its users are content creators who either already know about command lines and folders, or are willing to learn enough to run the script. Moreover, some thumbspage customization requires writing code that's beyond the scope of a GUI (see its header and footer inserts); and GUIs, especially the portable kind, have a way of being much more effort than you may think. In short, a GUI seems an unwarranted can of worms that would consume resources better invested elsewhere. On the other hand...
In the end, thumbspage is really about trying to do something useful in the stunningly ad-hoc and convoluted domain that is today's web development. As usual, fork with care. Just because you can save the village, doesn't mean you should.
You've reached the end of this guide, but there's more to the thumbspage story:
docetc/more-docs-trimmed
.
And may all your monkeys be right-side up.