GeezerVison is showing learning-python.com — thumbspage - User/Developer Guide
|
thumbspage — Turn Folders into HTML Image Galleries |
Welcome to the thumbspage manual. This guide is meant for content creators
and consumers of all kinds.
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 user, you'll find
resources here to help you get started building and viewing 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
to test-drive the original use
case,
and take a brief tour of this program at its
web page.
Contents
- 3.0 Quick Start
- Overview
- Viewing Galleries
- Building Galleries
- Version History
- 3.0.1: Android Chrome Fix
- 3.0: Tags, Keys, Swipes
- 2.3: Notes, Inputs
- 2.2: Swipes, Configs
- 2.1: Thumbs, Layout
- 2.0: Auto, Top, Full
- 1.7: Info, Exifs, Etc.
- 1.6: Scaling, Rotation
- 1.5: Viewer Pages
- 1.4: Mobile
- 1.3: Unicode
- 1.2: Styling
- 1.1: Subfolders
- 1.0: The Basics
- Development Notes
- More Resources
3.0 Quick Start
Because this program has evolved over time,
this section summarizes the current state of using and building
galleries as of thumbspage's latest version, 3.0.
Later sections of this guide go into more details on this summary's topics.
For more context, also see the 3.0
demo and
screenshot galleries.
Viewing Galleries
Thumbspage galleries work on all commonly used desktop and mobile browsers,
both online and offline. View a gallery by opening its main file, normally
named index.html. You can open this file by visiting the gallery's
web URL in your browser or by clicking its local version in a file explorer.
The index.html file is known as the index page; its thumbnails open
viewer pages for individual images when tapped.
Depending on the gallery, both page types may be colorized per the light or dark
display-mode setting on your device. Change your mode to see the effect.
The next sections cover these pages' usage.
Index Pages
An index page
displays thumbnails for all the images in a
gallery. Depending on how a gallery is built, resizing the window
or display either reorganizes thumbnails to fit the new space
or makes them accessible with horizontal scrolls as needed.
Scroll index pages vertically to view more thumbnails, using your device's
normal scrolling techniques (e.g., swipe, bar, or mousewheel). Click the
Top button at the bottom when it's available to jump to the top
of the index, and tap any thumbnail to open an image's viewer page.
Viewer Pages
A viewer page
displays one image in the gallery, scaled to fit
the view. Resizing the window or display automatically rescales the
image to fit the new available space. In a viewer page, navigate and
invoke other actions with:
- Taps on the display's label, image, and toolbar buttons
- Swipe gestures on touchscreens, touchpads, and mousewheels
- Keyboard key presses, where a keyboard is available
About swipes:
For touchscreens, perform swipe gestures on image itself, using one
finger. For touchpads and mousewheels, the location of the mouse cursor
does not matter for swipes, and swipes may use two fingers on touchpads.
Touchpad and mousewheel swipes are more laggy than touchscreen swipes
or keyboard presses and best kept short.
Also note that your system settings may invert swipe directions.
A down swipe, for instance, may mean swiping toward either the
bottom or top of your display, depending on your settings, and
the meaning of left and right swipes can be swapped by both
your settings and gallery builds. Experiment with swipes on your
device to see which actions they trigger.
The following actions are available on all viewer pages:
- Back to index:
Index tap, i key, browser back
-
To return to the index page, tap
Index, press
keyboard key i, or use your browser's back action
(a button or gesture also called Back).
Your browser's back action returns to your prior position in
the index page, and the viewer page's button or key returns
to index-page top.
- Navigation:
Next/Prev tap,
left/right swipe, n/p key
-
To move to the next or prior image, tap
Next or
Prev, perform a left or right swipe gesture, or
press keyboard keys n or p.
Messages appear when navigation wraps around to the first or
last image in the gallery, and some galleries may have
end-of-gallery pages.
- Slideshow:
Auto tap, a key
-
To run a slideshow that advances to each next image after a preset
delay, tap
Auto or press keyboard key a.
Repeat to stop the slideshow. Slideshows are an automatic Next
with a delay between images and continue until toggled off.
- Fullscreen:
Full tap, f key
-
To open the page in temporary fullscreen mode, tap
Full
or press keyboard key f. Repeat to close fullscreen mode.
This is a quick zoom but lasts for just one page. Some browsers have
more permanent fullscreen options (e.g., F11 on Windows and Linux,
shift+command+f or upper-left green button on macOS, Hide Toolbar
on iOS, and icons on some Android browsers).
- Image note:
Note tap, up swipe, t key
-
To view a note describing the image where available, tap
Note,
perform an up swipe on the image itself, or press keyboard key t.
Close the note with the popup's bottom OK button, the keyboard
Enter key, or a tap anywhere outside the note. Notes remain
open for further reading until closed by the user, even after navigating to
the index or other images, or returning from a link.
- Image info: label tap, down swipe,
? key
-
To view info about the image (from Exif tags, file, and display),
tap the image label at top of page,
perform a down swipe on the image itself, or press keyboard key
?.
Close the info with the popup's bottom OK button, the keyboard
Enter key, or a tap anywhere outside the info.
- Raw view: image tap,
. key, up swipe?
-
To view the image in raw mode in the host browser, tap the image
itself or press the keyboard
. key. This action is also
triggered by an up swipe when no image notes are present. Use the
browser's back action to return to the gallery, per the popup reminder message.
Building Galleries
Thumbspage is a Python command-line program distributed in source-code form.
To use it to build a gallery, first download and unzip its code
folder,
and install any Python 3.X
and its free
Pillow
extension. Then, run the unzipped folder's thumbspage.py
script in your console (e.g., Terminal, Command Prompt, or app), providing
gallery configuration options in any of the following forms:
- The
user_configs.py file's assignments
- Command-line arguments of the form
option=value
- Console inputs prompted for main options not provided otherwise
The next sections go into more detail on gallery configuration.
Configuration Settings
The user_configs.py file resides in
the program's own install folder. It defines all available configuration options
as Python assignment statements (option = value) and documents
their roles and values. The names assigned in this file are the
options' names.
Each option can also be provided as option=value
arguments in the thumbspage.py command line, which override
assignments to same-named options in user_configs.py.
For legacy reasons, a handful of primary options are asked at
the console if their corresponding option remains set to None
in user_configs.py and they are not provided as
option=value arguments.
These inputs all have option names in the user_configs.py file
(e.g., inputThumbMaxSize); set them in that file or pass as
arguments to avoid console inputs (e.g., inputThumbMaxSize=128).
As a special case, if a first argument is passed to
thumbspage.py and it is not of the form
option=value, it is assumed to be the
pathname of the gallery's folder, where your images and configuration
files reside. This can also be provided as option name
inputImagesFolderPath via user_configs.py
or command-line argument.
In sum, option configuration is layered as follows:
user_configs.py provides the preset defaults for options of
all builds, and can be changed by users freely.
- Command-line
option=value arguments may provide
per-build overrides for any of the preset defaults in user_configs.py.
- Console inputs are used for main options whose names are not set
in either
user_configs.py or the build's command-line
arguments.
- The first command-line argument is assumed to be the image folder
pathname, if it is not of form
option=value.
Command-line Examples
In the following example command lines, thumbspage.py
may be the full pathname to the program's main script, and Windows
typically spans lines with ^ or ` instead of
\ and uses py or py -3 instead
of python3:
Use options in user_configs.py, ask if console inputs are None:
$ python3 thumbspage.py
Also provide the image folder's pathname as the first argument:
$ python3 thumbspage.py folder
Also provide arguments that override user_configs.py settings:
$ python3 thumbspage.py folder option1=value option2=value
Same as prior, in shells that support spanning lines with "\":
$ python3 thumbspage.py folder \
option1=value \
option2=value
A larger example with explicit options and no console inputs:
$ python3 ~/Downloads/thumbspage/thumbspage.py . \
useDynamicIndexLayout=True \
dynamicLayoutPaddingH=\'16px\' \
inputCleanThumbsFolder=True \
inputThumbMaxSize=128 \
inputUseViewerPages=True \
popupFgColor=\'wheat\' \
popupOpacity=0.45 \
omitIndexPageLabels=True
Provide console inputs from a file using stream redirection:
$ python3 ../thumbspage.py mypix popupFgColor=\'#ddd\' < inputs.txt
Provide console inputs with a "here" document in Bash and others:
$ python3 ../thumbspage.py mypix popupFgColor=\'blue\' <<-EOF
(128,128)
EOF
Configuration FilesIn addition to named configuration options, you may also optionally configure thumbspage galleries by: Providing imagename.ext.note text files in the images folder, which give the note text for the corresponding image file (see also the NOTES.py alternative ahead) Providing custom HEADER.html and FOOTER.html files in the images folder, whose code is added at the top and bottom of generated index page and replaces default code Providing ORDER.txt, NOTES.py, and CAPTIONS.py files in the images folder, to give image ordering, notes, and labels, respectively Editing the program's own template-*.html files, used to construct pages in the generated gallery and vital to this program's operation (please edit with care) If used, HEADER.html and FOOTER.html contain the HTML code used above and below the thumbnails table in index pages. They are advanced tools describedahead.
ORDER.txt lists one filename per line to give an
explicit image order, and NOTES.py and CAPTIONS.py
contain a Python dictionary mapping image filenames to notes and labels.
If provided, these files' content overrides and augments filename
ordering, .note text files, and filename labels.
Whether coded in .note files or the NOTES.py
dictionary, image notes may embed HTML tags and entities passed to the
browser when escaped with a leading underscore. Notes are optional.
See user_configs.py and later
sections of this User Guide for additional details on all of the above.
Testing and Publishing
After running thumbspage.py to build a gallery, open the
image folder's generated file index.html (its default name)
in your browser to view the resulting gallery. To publish a
built gallery, simply install the entire image folder on the local or
remote host, with a copy, zip+unzip, or upload as needed.
Include the image folder's generated _thumbspage subfolder,
which contains scaffolding files needed to view the gallery.
For more ideas, see the 3.0 demo's
build and
publish scripts.
Overview
This section introduces the basics of thumbspage's roles and operation.
Read this first if you're looking for a quick summary.
Why thumbspage?
In short, this program allows you to view or display a folder of images in a
web browser, using a format which is simple by design yet noticeably better than
browser defaults, and does not require a web server except when viewing
images online.
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 broadly
customized
by content creators.
The mostly static results can be viewed offline or online in any web
browser.
In a theoretical sense, thumbspage is a program that builds another program:
its results are a combination of HTML, CSS, and JavaScript, which are run
by a browser. More tangibly, thumbspage simply creates presentation
scaffolding for user-friendly views of a folder's images.
What thumbspage Does
In a bit more detail, given a folder of image files, this program generates an
HTML
index page in one of two
flavors, with generated
thumbnail links for each image in the folder.
This page's links open the full-size images using either
browser-native (i.e., built-in)
display,
or generated HTML
viewer pages.
Viewer pages in turn dynamically scale the image to your display; include links for
gallery navigation, automatic
slideshows,
and one-page
fullscreen;
and open image-info
popups
on filename clicks,
and image-description
popups on button taps when
enabled.
On mobile and PC touch screens, swipe gestures on the image trigger
viewer-page actions too.
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 both websites and non-web use cases,
including program documentation, photo sharing, and general viewing.
When run using the techniques explored ahead,
the builder program skips non-image files; uses optional header and footer HTML
inserts in the index page; uses text note files for any or all images in viewer pages;
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.
The resulting
gallery
is complete and self-contained, and ready to be viewed or published.
Using thumbspage Galleries
After running this program, you can view or publish its results in
a variety of ways:
- To view results
-
Use any web browser to open the generated index page created in
your images folder (the page is named
index.html
by default).
- To publish results
-
Copy the entire images folder, including its
generated thumbs subfolder and index file (named
_thumbspage
and index.html by default, respectively).
- To publish results to a remote website
-
Upload the entire images
folder—index page, images, and thumbs subfolder—to the folder
representing your gallery on your site's web-server host.
Zip or otherwise
bundle the folder first for convenience.
As a client example, the website
that hosts thumbspage uses it for some 50 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.
Builders can also find gallery-build pointers in thumbspage's publishing
scripts, though its simple console
interaction or
command lines may suffice for most uses.
Latest thumbspage Features
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:
- As of 3.0, there is support for
omitted filename
extensions,
end-of-gallery indicators and
pages,
swipe gestures on touchpads and mousewheels too,
embedded HTML tags and entities in
notes,
keypresses where available,
raw-view
messages,
explicit ordering and
captions,
and a precoded dark
mode.
- As of 2.3,
viewer pages gain an optional Note
button
which opens an image
description;
up swipes now invoke Note when this feature is
enabled;
index pages can omit filename
labels;
tooltips have been enhanced and are now
enabled by default;
config-file settings or arguments can replace console
inputs;
and more.
- As of 2.2,
viewer pages support touch swipe gestures on displayed images (left/right go to
the next and previous image, and down/up open image info and raw
view);
setting=value command-line arguments override config-file
settings;
tooltips can be enabled on all
pages;
and more.
- As of 2.1,
thumbnails can be enhanced by color, contrast, brightness, sharpness, and
save quality, with presets
that remove JPEG compression noise and resize
blur,
and add a black-and-white
mode;
a new dynamic-layout alternative for index pages
debuts;
and builds are automated.
- As of 2.0, viewer pages gain an Auto button that toggles automaticslideshows,
a Full that toggles one-page
fullscreen,
and custom dialogs and device lines for
info popups;
index pages sprout floating
Top buttons;
and all pages have improved styling for handling rare page
overflows.
- As of 1.7,
viewer pages
open info popups on
filename taps
and browser-native views on
image taps,
avoid clipping for
large fonts,
and drop hover effects spotty on mobile;
and GIFs get better
thumbs,
rotations keep and update
Exif tags,
and iOS landscape images are
scaled.
- As of 1.6,
JavaScript is used to
dynamically scale
viewer-page images without changing aspect ratio or overflowing pages,
and tilted images are
automatically rotated
to display right-side up.
-
Earlier releases added
formatted
image-viewer pages [1.5];
support for viewing galleries on
mobile devices [1.4];
support for filename and content
Unicode [1.3];
and more.
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.
Viewing Galleries
This section covers the basics of viewing the galleries which thumbspage creates.
It's oriented towards gallery consumers (a.k.a. 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
Index pages are the usual entry point into a gallery.
Here's one captured on
desktop and
mobile browsers,
and its live
version.
Index pages display thumbnail images, either in a fixed table which scrolls both
horizontally and vertically as needed, or arranged to fit the display's size per
the 2.1 update ahead. Either way, index-page thumbnails normally open the next section's
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
nested in the page's folder, 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, so it may not show up on smaller index pages.
An index page's subfolders links
may lead to supplemental info or nested galleries, and their behavior varies per
usage mode. When viewing galleries online, subfolder links typically open
a nested gallery's index page automatically. When viewing
offline, these links open a simple directory listing, and you'll
manually click nested galleries' index pages (e.g., index.html)
if present. These links also open directory listings for non-gallery folders
on some online servers; tap filenames to open. There's more on subfolders
ahead.
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, may display tooltips on
mouseovers,
and can opt out of either floating Top buttons or subfolder
bullet lists. Experiment with galleries you visit to see which options they support.
Update:
as of version 2.1, thumbspage index pages may also use either a fixed
or dynamic layout. By default, index pages use the original
fixed
layout, which always displays the same number of columns. In
dynamic
layout, the thumbnails table
does not scroll horizontally; instead, the number of columns is chosen to match the
display size, and changes when the display is resized.
Dynamic layout is an option which can be enabled when a gallery is built.
If you run across this alternative, you can shrink or expand your window
on PCs and mobiles with popup app windows to view fewer or more columns.
On mobile in general, switching device orientation has a similar effect.
See the demo.
Viewer Pages
Index-page thumbnail clicks and taps 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,
along with its live
version.
Viewer pages display the image's filename, the image itself, and a bottom toolbar
of action buttons, and respond to swipe gestures on the image on touch screens.
The image is automatically scaled to fill the window's or display's available space,
and grows or shrinks in both directions with desktop-window resizes, mobile-device
rotations, fullscreen toggles, and popup or split-screen app-window resizes on some mobiles.
The toolbar has four to six named action items (Note and Full are optional),
and scrolls horizontally to reveal buttons if needed on
smaller displays.
Viewer pages may display tooltips on
mouseovers
too; this is enabled by default when galleries are built.
Everything on a viewer page does something when tapped and images
respond to swipes, as the following sections will describe.
Widget Actions
All told, there are seven or eight actions available to users on viewer pages,
five of which have swipe-gesture equivalents—and
two of which are invoked with display widgets instead of buttons:
- Filename tap — view image-info popup
-
Open an
info dialog
describing the image. This displays the image's date of origin;
date last modified; file size; and image and display dimensions
(as width x height pixels).
When date of origin is known, "Taken" means a camera capture,
and "Digitized" implies a
scan.
If known, the popup also lists the origin device (e.g., camera or scanner)
or software (e.g., drawing program). The info popup appears at the top of
the page; scroll its text or page as needed on smaller displays, and tap its
OK button—or anywhere outside the popup's window in most browsers—to
close the dialog.
As of 2.2, the filename-tap event is also triggered by a down swipe on the image.
- Image tap — view raw display
-
Open the image using the underlying browser's
raw display.
This display is useful for a zooming in and out (e.g., with spread/pinch, or
control or command with +/-),
and may yield a larger image depending on browser and display size.
Use the browser's back action (button or swipe) to return to the gallery's
viewer page. The Full toolbar button also zooms the image in most contexts, but
we have to move ahead to see how.
As of 2.2, the image-tap event is also triggered by an up swipe on the image in
supporting browsers when image notes are unused (see ahead).
Toolbar Actions
The remaining viewer-page actions can all be requested by tapping named
toolbar buttons
(or swiping equivalently), and one is optional and may be omitted when a gallery is built:
- Prev — view previous image
-
Go to the previous image, in filename-sorted order. By design, the current image
is not 'stacked' in browser history, which means it won't be revisited on a browser Back
request; you'll jump immediately to where you where before the first image view.
As of 2.2, Prev is also triggered by a left- or right swipe on the image.
- Next — view next image
- Go to the next image, in filename-sorted order. Just like Prev, the current image
is not 'stacked' in browser history on Next, so it won't be revisited on a browser
Back request; you won't have to retrace through every image you've viewed.
The Auto button covered ahead automatically runs Next. As of 2.2, Next is also
triggered by a left- or right swipe on the image.
- Index — view thumbnails page
-
Go to the thumbnails
index page
of this gallery. This is usually equivalent to a browser Back
request unless the gallery was entered at a specific image, except that
the image-viewer page is remembered in browser history so you can return
to it with a later Back.
- Note — view image-description popup
- Open the image's note, if one is available. As of 2.3, galleries can use notes to describe individual pictures onviewer pages.
This adds a new component to galleries which choose to use it.
To view an image's note, click on the toolbar's Note
button,
or up swipe
on the image itself on a touch screen. The note appears as a popup
window at the bottom of the page, and can be dismissed by tapping its
OK button, or anywhere outside the popup on most browsers. Images
without a note always have a
line-through
on their Note button so you can skip them, and notes can be scrolled
vertically for smaller
screens and larger notes.
Keep in mind that this feature is optional and new: Note buttons will be
present and up swipes will open notes only if notes were enabled at
gallery build time. If notes are unused, up swipes open raw views as in
2.2; if notes are used, you can still open raw views with an image tap.
Also note that Note popups don't stay open during Auto slideshows;
tap Auto to stop and read a note along the way, per the next item here.
- Auto — start/stop slideshow
- Toggle the automatic slideshow on or off for thisgallery.
Slideshows run
toolbar Next operations in time-delayed mode, automatically advancing to each next
image after a fixed pause that may vary per gallery (it's 5 seconds by default).
Use this to step through images without manual navigation; like Next, pages you
visit aren't added to browser history, so you need not retrace your slideshow
path later on browser Back requests.
When active, a slideshow applies to all thumbspage viewer pages visited in a
single browser tab. They stop (really, 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 or any other
navigation 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).
Update:
as of version 2.1,
the Auto button changes to be underlined as an indicator whenever the slideshow
is on,
and changes back to normal text when the slideshow
is off.
This helps make the show's state apparent, especially when it's first started
or is resumed on return from another page.
- Full — open/close one-page fullscreen (optional)
-
Toggle a page-induced fullscreen display on platforms that support one, for this page only.
When present, the Full toggle
maximizes the browser window and hides all browser and platform toolbars; another tap
shrinks back to normal size.
Some browsers and platforms don't support this fullscreen mode (e.g., iOS),
and on all those that do, the fullscreen display lasts for just the current window: any
navigation to a new image page cancels fullscreen mode—including Next, Prev, and
automatically moving to the next image in an Auto slideshow. This obviously limits what
you can do with Full.
Because of these limitations, the Full button and feature may be omitted on
a per-gallery basis (it's enabled by default). 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 tip for pointers.
Note that Full may be hidden on small displays; scroll the toolbar left as needed.
Update:
like Auto, the Full button changes to be underlined as of 2.1 as an
indicator that its fullscreen mode
is on, and
resets its font when its fullscreen is cancelled, whether by Full or outside the page
(e.g., with an Escape key). Unlike Auto, though, fullscreen mode is obvious, and Full's
font changes are not supported on all browsers. Moreover, even where supported, Full
will not change for, and will not cancel, a fullscreen mode started independently of
the page; for better or worse, user-initiated and page-initiated fullscreens differ,
and browsers handicap the latter in the name of security.
Swipe Actions
Beginning with version 2.2,
thumbspage viewer pages respond to swipe gestures
on all devices and browsers which support them. Swipes require a touch screen
(update: touchpad and mousewheel swipes are also
supported as of version 3.0),
but they work broadly on both smartphones and
Windows PCs, and in all dozen browsers tested except Internet Explorer (subject
to one up swipe limitation covered ahead).
To perform a swipe, start the gesture in the image-display area for
touchscreens—that is, on the image itself—in any
viewer page. Touchpad and mousewheel swipes may be performed
anywhere. The physical direction of the swipe depends on system settings,
as noted in the 3.0 Quick Start,
but the logical direction of the swipe determines its action:
-
Down swipe — view info popup
-
This gesture opens the image-info
dialog
at the top of the page,
and is the same as tapping the filename. As
noted above, close the popup by tapping its OK, or anywhere outside the
popup box on most browsers.
- Up swipe — view image note or raw display
-
This gesture invokes one of two actions, depending on how the gallery was
built:
-
Note:
if image notes were enabled when the gallery was made,
up swipe opens the image's description
popup at the bottom of
the display, and is the same as
tapping the toolbar's Note button described above.
Just like Info, a tap on OK (or outside the popup on most browsers) closes the popup.
This flavor of up swipe works on all desktop and mobile browsers, and is
automatically used for an entire gallery when any image notes are available.
-
Raw:
if image notes were not enabled, up swipe instead opens the image in the browser's
raw view,
and is equivalent to tapping the image.
Due to a glitch in current versions of Chrome and its derivatives, this flavor of
up swipe is at present enabled only for Firefox on all platforms, all browsers on iOS,
and Samsung Browser on Android.
Other browsers on touch-display devices respond to this gesture
with a message
popup.
For more on this limitation, see the implementation note
ahead.
-
Left swipe, right swipe — view next and previous image
-
These gestures move forward and backward through the
gallery,
just like tapping the Next and Prev toolbar
buttons.
The direction of movement depends on both user settings and a configuration setting at
gallery build time, so try a few swipes to see how
they step through the galleries you view. Per the preset default,
left swipe means next image (Next), and right swipe means previous image (Prev).
You can test-drive swipes on a touch-screen device by running any of the
demos listed here.
Where available, swipes are simply a convenient alternative to other page actions.
All former widget and toolbar taps described above
still work as they did in prior versions, as do browser long-presses,
pinch/spread zooms, and back/forward swipes.
Mouse clicks naturally still work on non-touch displays too.
Usage tip: some browsers implement back/forward
swipes as an alternative to buttons. thumbspage left/right swipes won't clash
with these as long as both are performed well. Be sure to trigger browser
back/forward with a short flick from screen edges, and begin thumbspage swipes
in the page's image display. If this is difficult, you can also do back/forward
by swiping above or below the thumbspage image-display area, or simply use browser
back/forward buttons.
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.
The next section provides more info on gallery views.
Viewing Tips
This section collects additional assorted tips for viewing thumbspage galleries.
Content you'll find here:
- JavaScript requirement
-
Although thumbspage galleries work in any
unextended browser, they do require that the browser's JavaScript
support be enabled if you wish to use index pages' floating Top buttons,
as well as viewer pages' navigation, image scaling, info popups,
notes, swipes, automatic slideshows, and one-page fullscreen display.
Both types of pages still function without JavaScript, but these features
are either not displayed or inoperable when JavaScript is disabled.
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.
Update:
as of version 2.3, JavaScript is now fully required for using the
viewer pages created
by thumbspage. This is normal on the web today, and was already effectively
required in prior versions, because images did not scale and most widgets were
inoperative. 2.3 made it official, with an enhancement that renders navigation
buttons no-ops without JavaScript too, leaving no usable content. For more
background, see the builder's note.
- Browser and device support
-
Short story: thumbspage galleries should work well on all browsers and devices
in common use today (and a few in uncommon use too). Galleries have been explicitly
verified on dozens of browsers—including multiple versions of
Chrome, Firefox, Safari, Edge, Internet Explorer, and Opera,
running on multiple versions of macOS, Windows, Linux, Android, and iOS.
Galleries have also been verified on a range of PC and mobile devices,
including displays as small as
4 inches.
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.
- Enable iOS 13's Hide Toolbar for Safari
-
If you're using Safari on an iPhone, iPad, or iPod, thumbspage
galleries are best viewed with iOS 13's Hide Toolbar option enabled.
This option is available in the
"aA"
menu at the upper left of Safari's
window,
and is applied to all pages opened in an enabling tab, including all those
in an Auto slideshow.
Without this option, Safari reports sizes poorly on iOS, which
makes thumbspage image-viewer windows require a scroll in landscape
mode; with this option, both landscape and portrait work
better,
and links don't require an extra tap.
Other browsers on iOS, including Chrome, don't have these issues.
- Fullscreen manual options
-
As noted earlier, the Full button on thumbspage viewer pages is optional, because it lasts for just one page, and is supported unevenly across browsers. By contrast, many browsers offer a manual, user-initiated fullscreen mode that persists across page switches, including those of thumbspage Auto slideshows. Where available, this can be a much more useful alternative. The options for invoking a manual fullscreen are too many to cover here, but here's a quick rundown:
On Windows, the F11 key (with fn if needed) puts the browser in fullscreen mode, which persists for all pages visited until explicitly disabled with another F11. This works in Chrome, Firefox, Edge, Internet Explorer, and likely others, and allows thumbspage Auto slideshows to run completely in fullscreen mode. On Linux, the F11 key (with fn if needed) switches browsers to and from persistent fullscreen mode, just as it does on Windows. This works in Chromium, Firefox, and likely others, and, as on Windows, allows thumbspage Auto slideshows to run completely in fullscreen mode. On macOS, you can toggle a similar persistent fullscreen mode in Chrome, Safari, and Firefox, with the keys combination 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.- On iOS 13, Safari's Hide Toolbar option of the precedingnote
isn't fully fullscreen, but it's close enough to mention again here,
and lasts for all pages you visit (including those in Auto slideshows).
This alone makes it worth upgrading to iOS 13, if you already haven't.
-
On Android, numerous apps available from the Play Store promise
persistent fullscreen support. Judging from chatter on the web, though,
some may be better than others, so the usual due-diligence caution applies.
It may also be possible to force a browser app to use Android fullscreen
mode with a tethered ADB command or Settings option, but this may not
omit the browser's own UI baggage, and ADB seems well beyond most users'
patience.
See also the Opera update ahead for a new Android fullscreen option.
For additional tips and other platforms and browsers, try a web search,
or consult your browser; browsers change regularly, and new fullscreen
options may crop up over time.
Manual and persistent fullscreen isn't available everywhere today, 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
browsers will make Full fully superfluous in the not-too-distant tomorrow.
Update: on Android, the Opera web browser
recently gained an amazing fullscreen mode for landscape that works much like
those in desktop browsers, and spans multiple pages. For details, see this
usage note
at thumbspage's host site. The Opera browser is largely the same as Chrome under
the hood,
and runs thumbspage galleries just as
well—and
arguably better with its landscape fullscreen.
Yet another reason to consider parting with the herd on Android.
- (Defunct) iOS Chrome may stack navigation history
-
thumbspage image-viewer pages are not normally stacked (i.e., remembered)
in browser history as you—or a slideshow you started—navigate through
galleries. This by design, because it makes browser Back operations return
to the viewer pages' entry point immediately, instead of stepping back though each
image viewed. This may not work in some versions of Chrome on iOS, unfortunately,
due to a bug in that browser. That bug appears to have been fixed in Chrome 83
(or earlier), but was known to still be broken as of Chrome 75. If your Chrome
stacks pages, either retrace pages as needed for that browser's Back,
or use any other iOS browser to view thumbspage galleries.
- Viewing galleries offline on PCs
-
thumbspage bills its galleries as viewable both online (at a website) and
offline (on your device). In more detail, online mode works universally on all
devices, and offline is trivial on PCs (which means Windows, Linux, and macOS here)—a click
in a file-explorer window (e.g., Finder or Explorer) normally suffices to open a gallery
in your default browser, and right-clicks, browser Open functions, and explicit
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. macOS Safari users: see also the
caveat ahead for the rare use case of
browser Back returns to offline galleries.
- Viewing galleries offline on Android
-
(TL;DR: jump to the latest
update for new file-explorer options.)
On some unrooted Androids, you may have trouble viewing a gallery stored
locally on the device: the index page may open in a file explorer, but its
thumbnails and viewer pages won't. This happens mostly in Chrome on newer
Androids, and seems related to that platform's push to tighter
permissions and
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:
- Move it to internal storage instead of SD card (e.g., on Pie and earlier)
- View it with a different browser (e.g., Firefox, Opera or Samsung instead of Chrome)
- Open it with a different file explorer (e.g., Total Commander, and tap
file://url if present)
- Open it via a manually typed file URL (e.g.,
file:///sdcard/...)
- Ditto, but try different pathnames in the URL (e.g.,
/storage/emulated/0; see the
primer)
- Move it online if possible, and open with a web URL (e.g.,
https://...)
Especially on newer Androids, also make sure the browser has permission to access
your files in storage. Firefox, for example, may require this on Android 10.
Try Settings/Apps and navigate to storage permissions, or similar on your device.
This has grown more crucial as later Androids have moved to lock down storage to
app-specific
sandboxes.
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.
Update:
for recent findings on both local-file views and running web-server
apps on Android, see the off-page coverage
here,
which is part of an Android 11 review. In short, you can run a web server on Android,
but your functionality may be limited and your disconnections many. Android 11 has
also narrowed your options for local-file views in browsers, in the name of a
security rationale; inoperable phones may be secure, but they're also inoperable.
Update:
the thumbspage team recently discovered another route to offline viewing on Android.
On Android 11, simply copy your content into the browser's own sandboxed folder, using
a file explorer that has permission to do so (Solid Explorer and Cx File Explorer do
at this writing). Then, use that folder's local path in a file:// URL
in the browser to view offline. For example, copy content to these locations for
Chrome and Opera (alas, this won't work for Firefox on 11, because it strips
the file://):
file:///storage/emulated/0/Android/data/com.android.chrome/your-folder-and-file-here
file:///storage/emulated/0/Android/data/com.opera.browser/your-folder-and-file-here
This works for Chrome on Android 10 too, but isn't required for Opera or Firefox on 10,
where they have full access to arbitrary storage paths
(unlike both Chrome on 10, and all browsers on 11).
This isn't the same as arbitrary file access, of course; you're limited to files dropped
into a browser's folder. This is also an arguably terrible idea: your content
in the sandbox folder will be deleted if you uninstall the browser, and is prone to being
nuked if the browser ever resets its own folder. Hacker beware!
Update:
in late 2021, some Android file explorers now provide seemingly heroic work-arounds on
Android 11 that allow you to use a browser to view web pages stored locally on your
phone—including thumbspage galleries. For example, among file-explorer apps tested:
-
Cx now automatically
spawns an HTTP server to open local HTML files, and launches your browser to view
these files on the locally running server. This is not a full Apache web server (e.g.,
URL rewrites and auto-indexes won't work, and CGI scripts are displayed instead of run),
and local folder listings are explicitly disabled by the server. Still, this works well
for basic views of much client-side content, including galleries generated by thumbspage.
-
Solid, among others, now opens local HTML files in browsers with suitable
content:// URIs that implement Android's content-provider paradigm.
This scheme is not a substitute for a web server, opens some complex pages slowly, and
can break down for some nested website content (e.g., folder indexes may oddly invoke
a download that fails). But it also suffices for viewing basic content and thumbspage
galleries on your phone.
Thus, your first step when trying to view locally stored thumbspage galleries
on Android 11 and later today is to simply try opening them in a file-explorer
app to see if they are supported automatically. For more details on the
constantly evolving struggle between Android permissions and file explorers,
see also the updates
here,
here,
and
here.
- Viewing galleries offline on iOS?
-
On unrooted Apple mobiles, iOS 13's enhanced Files app allows you to
open files both on USB drives and copied to internal storage. This lets
you view a gallery's pages, but doesn't open a browser. Instead, it runs
a stripped-down quick-view that lacks JavaScript and shows just the
top-level page's text, not any images it embeds. The net effect is
much like recent Android Chromes of the preceding note, and your most direct
recourse may be an online (or local network) upload.
While there may be additional iOS options
beyond thumbspage's testing budget, Apple has historically been
more closed than Android (and its appetite for control seems only to be
growing).
- Beware browser settings and bugs
-
Some browser settings that may appear harmless can adversely
impact thumbspage galleries. The native (a.k.a. raw) image display
of some versions of Android Chrome, for example, may initially
render some images weirdly large in portrait orientation, but if and only
if the seemingly unrelated Force enable zoom is selected in
Accessibility Settings.
The image-tap raw displays of thumbspage viewer pages trigger this effect,
but do not cause it—it also happens when visiting images' URLs directly,
outside gallery pages.
There's more on this issue in
search
results, while they last; its impact is broad, but likely to be reversed soon.
Nor is this an isolated case.
As another example, the thumbspage team recently found and reported a
settings-related bug in 2020's Android Firefox—though not
thumbspage specific, this browser sometimes leaves an odd blank
strip at the bottom of thumbspage viewer pages if you enable its
"Scroll to hide toolbar" option (more details
here).
Thumbspage's host site also reported a bug in version 53
of Android Opera—though it does not impact thumbspage galleries (and was
fixed in version 60), this browser strangely truncated the remainder of a page at a
/* character sequence in text formatted 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.
Building Galleries
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.
Examples:
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.
Installs and Platforms
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.html
The 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, macOS, 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 some 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 viewed, 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 via
search or
here,
though the -y suggested at the latter 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 command lines 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.
Running thumbspage
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.
Usage Example
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 macOS
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 for empty replies are
given in () (Windows users: be sure to use py -3 at the start
of your command line, \ instead of / in your pathnames,
and a C: or other drive letter if needed):
/.../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, "trnpix".
Prompts and Replies
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. Here are the
parameters you'll input to run thumbspage:
- Reply #1: images folder
-
This 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 just
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.
Special cases: a solitary . here means the directory where the script
is being run, which is also the default for an empty reply (i.e., pressing your enter
key only). You can also use other path syntax such as two dots for one level up
(e.g., ../folder is a sibling); see the pathname primer
ahead if you're new to such things.
Tip: if you name a folder which has no images,
you'll make an index page with a subfolder list.
- Reply #2: clean first
-
You should generally opt to clean (i.e., empty) the thumbs folder first with a
positive response for reply #2, unless images have only been added to the image folder
(in which case cleaning means remaking old thumbs, but still works in any event).
There's more on this option in the building tips ahead.
Note this and all the following replies are not requested if you're building
an index page for an imageless folder.
- Reply #3: row size
-
Replies #3 and #4 allow you to tailor index-page thumbnails on each run.
Additional thumbnail enhancements that may span runs are available as
configuration settings described ahead.
For reply #3, give the number of thumbs you want to display in each
row on the index page. The thumbs table automatically expands and shrinks
with the page, and scrolls both horizontally and vertically
where needed. Note that this input is not requested if you opt to
use 2.1's new dynamic index-page layout alternative,
because this layout determines the number of columns automatically from
page size.
- Reply #4: thumb size
-
For reply #4, give the requested pixel size of all thumbnails in the gallery.
Enter two numbers separated by a comma for width and height (in that order),
with optional spaces and enclosing parentheses. thumbs size input is a
maximum area for all thumbs: thumbspage creates thumbnails no larger
than this that retain their original aspect ratio. For instance, an input
100,100 makes thumbs 100x100 pixels large, but either side may
be rendered less than 100 large if needed to avoid distortion. There's more on
what that actually means in the sidebar at the end of this
section.
Exception: as of version 2.3, thumb sizes provided as configs by file or
argument can now give a single integer instead of a tuple, if both sizes
are the same. Sizes input at the console, however, must still be 2-tuples
as described here. See 2.3's update for details.
- Reply #5: viewer pages
-
Finally, you should generally elect to use image-viewer pages with a positive response
for reply #5, unless they don't work well in your use case. These pages
have matured much since their introduction, and have no apparent downsides today.
There's more on this option in the building tips ahead.
If you change your mind or make a mistake while inputting replies:
you can cancel a run by typing a break or EOF key sequence (e.g., hold
control and type a c or d) at any prompt,
and thumbspage reports input errors nicely as demonstrated in this
console log.
For more examples of console runs on various platforms, see the logs
folder.
Other Usage Modes
Caution: using precoded console inputs via stream redirection or "here"
documents as described in this section is now discouraged because inputs
may change—and did in 3.0, which made dynamic index layout the preset
default, reducing the number of inputs by one. To avoid build-script changes in
the future, instead pass input options as setting=value
command-line arguments, along with other per-build configs.
See the updates ahead and the full coverage
here and
here.
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 input parameters in a text file,
one per line (technically, by redirecting the stdin
stream).
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 input file inputs.txt
would like look the following; use an empty line to accept a prompt's default,
and omit the first line if you provide the folder name as an argument 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 input 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 inputs.txt
y
3
128, 128
y
$ python3 $C/thumbspage/thumbspage.py trnpix < inputs.txt > report.txt
$ more report.txt
...output here...
Finally, on Unix systems—and Windows systems that have Unix subsystems or
similar tools—you can also provide console inputs immediately
following a command line, by using "here documents" of the sort available in
Bash and other Unix shells. Using this in a script file allows you to code
canned input parameters in the script itself, without having to juggle a
separate file. The following demos the idea,
but see your shell's docs for more details if needed; in short, it assumes
$C is your install folder again, and provides canned inputs as tabbed
lines between EOF markers:
$ cat generate.sh
#!/bin/bash
python3 $C/thumbspage/thumbspage.py trnpix <<-EOF
2
128, 128
EOF
$ bash generate.sh # or just "generate.sh" if it's made executable via chmod
...output here...
Fine print: use just <<EOF and omit tabs if your shell
requires it, and don't expect a 2> /dev/null to suppress
input prompts here. The latter won't work, because Python's input()
function prompts to stdout (well, often; this is inconsistent,
and an open bug).
The tagpix program supports
prompt drops this way, but only because it uses a custom input() that
prompts to stderr. Such redirects are dodgy in any event;
messages for uncaught errors, including Python exceptions,
may also be lost in the bargain.
You can catch the last of these ideas in action in the shell and Python scripts
here,
here, and
here, used to
automatically generate thumbspage's example galleries.
thumbspage's full set of build scripts here may
also provide pointers for more advanced automated builds, including
automatic zips and uploads for external thumbspage clients.
Although thumbspage's basic console interaction covered
above
should suffice for many use cases, automating its launches
is straightforward, and may be necessary when thumbspage is used as
a nested tool in a larger build system.
For more background on the shell techniques shown here, try the off-site overviews of
streams,
redirection,
variables, and
here docs.
For more thumbspage usage examples, browse its
examples folder.
The console input prompts and arguments we've met in this section 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: thumbspage 1.7 added the optional
folder-name command-line argument. 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. 2.1 omits the thumbs-per-row input
when dynamic layout is chosen, because
the input is moot in this mode, and omits all inputs but the first as
irrelevant for imageless folders.
Update: thumbspage 2.2 added optional
command-line config arguments, of the form setting=value.
When used, these arguments are coded in the command line after the optional folder
path, and override same-named settings in the configs file.
With this extension, thumbspage command lines now take the following general form:
python3 thumbspage.py imagefolder? setting=value setting=value...
For full coverage of this feature, see the 2.2 release note.
Update: thumbspage 2.3 added new config settings
that can be used to override console inputs. When provided, in the config
file or command-line arguments, input values are taken from the settings, and no input
is requested at the console's stdin. For example, a command line of the following form
overrides two console inputs with arguments:
python3 thumbspage.py imagefolder inputCleanThumbsFolder=True inputThumbsPerRow=4
This provides are portable and uniform alternative
to the shell redirect and here-document techniques described in this section.
For more details, see the 2.3 release note and its command
examples.
As noted above, 2.3 also allows thumbs maximum size to be provided as a single
integer instead of a tuple, when given by configs; see 2.3's note.
A Brief Primer on Pathnames
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
prompt #1 can be either:
- A name that is relative to your current location in a console. Examples:
a simple name like
photos for an item in the folder you're working in;
. for the current folder itself; .. for one level up;
and ../photos for a filesystem sibling
- A name that gives an absolute—that is, complete—path
from your drive's root to a folder. Examples:
/Users/you/photos
on Unix; C:\Users\you\photos on Windows; and
/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=.)? imagefolderhere
Or, 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,
after a brief technical sidebar.
More on Thumbnail Size Inputs
For console prompt #4, thumbnails sizes are
input as a pair of numeric pixel dimensions, (width, height),
where parentheses and blank spaces are optional.
The underlying Pillow imaging library
always adjusts your inputs as needed to preserve the original image's aspect ratio,
instead of warping images.
Without getting into code, here are a few examples of how this works; thumbnail
sizes input on the left of =>
are automatically changed to sizes used on the right:
For an original image of size 100w x 50h (wider):
(300, 100) => (200, 100)
(100, 300) => (100, 50)
(50, 50) => (50, 25)
(100, 100) => (100, 50)
(300, 300) => (300, 150)
For an original image of size 50w x 100h (higher):
(300, 100) => (50, 100)
(100, 300) => (100, 200)
(50, 50) => (25, 50)
(100, 100) => (50, 100)
(300, 300) => (150, 300)
For an original image of size 100w x 100h (square):
(300, 100) => (100, 100)
(100, 300) => (100, 100)
(50, 50) => (50, 50)
(100, 100) => (100, 100)
(300, 300) => (300, 300)
You can see more results and experiment with this on your own using
this script.
In short, input dimensions you provide are essentially maximums:
they define an area in which to scale thumbs; they are only ever
decreased to match original images; and one may be adjusted down to keep
the aspect ratio the same as the original and avoid distorting the image.
If all that makes your brain hurt, remember this much: providing square
target dimensions (with the same width and height) seems the simplest path;
the common value will be the maximum for both dimensions of all thumbs in a
gallery. See also 2.3's update which allows
giving thumb size as a single integer in configs, when sizes are square.
Customization
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.
User Configurations File
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. Versions 1.7 and
later
add numerous options you'll generally find later in the configs file.
Updates: in addition to the configs file,
thumbspage 2.2 supports config command-line arguments, of the
form setting=value.
When used, these arguments override same-named settings in the configs
file, and provide an alternative and simpler way to customize settings
that may vary per gallery build.
For more on this feature, see the 2.2 release note.
Header and Footer Insert Files
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
-
Provides all of the page's HTML code up to the generated thumbnails
table (or Top button or subfolder links, if used). This can be useful for
adding page-specific content. For instance, header text can be used to
describe the images on the page and provide related links,
as in this example.
Custom headers naturally assume some knowledge of HTML coding, but need not
be complex, and can follow boilerplate examples (tip: generate a default
index page for header code to start with). Here are the fine points on coding
a header insert file of your own:
-
Custom headers should include a full HTML preamble, with
<!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.
-
Custom headers may also wish to include left and right
<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.
-
If enabled, code for an automatic floating Top button and a subfolder-links section is
inserted after custom-header text. This works normally and usually requires
nothing of headers, but some may wish to reference the subfolder list in their preambles'
narratives,
or create a list manually. See ahead for more on
Top and subfolders.
-
A Unicode type listed in a custom header's content-type
<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
-
Provides the remainder of the page's HTML following the generated
thumbnails table. It should add any post-table content and close the
page with both
</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.
More Header Options
The generated thumbnail index table's code is self-contained, and requires
no support code in a custom header.
Conversely, a custom 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). The way you'll do this depends on the
index-page layout model chosen:
-
In thumbspage's original and default fixed-layout model,
image labels on index pages can be customized by styling the
<td> element for the entire index page, using
CSS code like this in a custom header file's <head>:
<style> td { font-weight: bold; } <style>
-
In thumbspage's new dynamic-layout alternative
added in 2.1, image labels on index pages can be customized by styling
the id tag generated for the thumbnail-display's main
<div>,
with CSS code of this sort in a header file:
<style> #thumbslinks { font-style: italic; } <style>
-
In either layout model, you can set the font for the entire index page
by styling the body element as in the following; if used, the two options listed
above will override this for just thumbnail-image labels on the page:
<style> body { font-family: Arial; } <style>
For an example of this technique in action, see the
online demo site
here.
Beginning with 2.0, it's also possible to code scripts that replace an HTML
comment automatically added to the <head> of default index headers
and precoded in viewer pages for this purpose. See this note
ahead for more details, as well as this script
for an example that leverages this hook to insert analytics code in generated index
pages prior to uploads.
More generally, this lets programmers add arbitrarily custom code to default index-page
headers, and may avoid custom headers in some use cases.
Viewer-Page Template File
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, analytics code,
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.
Starting in 2.0, you can also customize viewer pages by
programmatically replacing the automatic <head> comments noted in
the prior section and described here. Though this requires some
programming skills, it is probably a
safer option than changing the viewer template in place, especially if you
ever upgrade to a new release (your edits won't be overwritten by an unzip).
Floating-Top Template File
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.
Image Note Files
As of version 2.3, you can optionally provide plain-text note files for
any or all of your image files, to provide descriptions or narration
for the images in your gallery. These notes are displayed as a
popup in
viewer pages when users tap the tollbar Note
button,
or up swipe on the image on touch
displays.
When present, note files appear alongside images in the images folder,
and are named with a .note extension following the
corresponding image file's name. For instance the note for an image
file photo1.jpg should be named photo1.jpg.note.
Note files may contain any Unicode text, including emojis, though HTML
code is shown verbatim (links won't work). Lines are collapsed into
paragraphs, with the exception that a blank line coded as two adjacent
line breaks (i.e., \n\n) is taken to be a paragraph
separator, and renders as a blank line in the popup.
This feature may be disabled, and Note buttons and their up swipe
appears only if this is enabled and at least on note file
is present in the images folder. For a demo of notes in action,
see this
gallery.
For more details on notes, see the 2.3 release note.
Related: if you use notes, you may also be interested in 2.3's
thumbs-only index pages.
Summary: thumbspage Folders
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 image files...
Optional image.note 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. Exceptions:
HEADER.html, FOOTER.html, and image
.note files, if used, are required at gallery build time only,
and need not be present when a gallery is viewed. This follows from the fact
that header and footer contents are embedded in generated index pages,
and image notes are embedded in generated viewer pages. Shipping these
files to content consumers is optional.
You can view raw examples of generated index and viewer pages and
_thumbspage/ folders online
here,
here, and
here,
and their live renditions here.
In addition to the images-folder content sketched above, 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.html
Edit 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.
Updates: in addition to the files and options
covered in this section, version 3.0 adds a new program-folder
template file for dark mode, template-autothemes.html, along with the
optional image-folder config files ORDER.txt, NOTES.py,
and CAPTIONS.py, which give
image ordering, notes, and labels, respectively. For more detail, see
the 3.0 Quick Start and
release notes as well as docs in
the user_configs.py file.
Building Tips
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):
- Viewer pages (or not)
-
The image-viewer pages added in version 1.5 are optional. If subpar
in your use case, they can be disabled via the last console reply
(see Running thumbspage above)
to use the former browser-native display.
As of 1.6, though, these
pages
scale displayed images well everywhere:
images grow and shrink with the window or display, without changing
their aspect ratio, distorting content, or overflowing their bounds.
As a fallback (and for better zooms in some contexts), users may also
click these pages' "Raw" links for browser-native view.
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 new "Auto" and "Full" buttons provide slideshows
and one-page fullscreen, respectively; see 2.0's release notes,
and use viewer pages.
- Image filenames text and length
-
Although thumbspage properly escapes and handles arbitrary image
filenames, those in your galleries should generally avoid characters
illegal on some filesystems (e.g.,
"), 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.
- Supported image types
-
Images in the source folder are detected by MIME
type (i.e., filename
extension).
All files there with an image MIME type are considered an image by thumbspage
and included in the generated gallery, whether they have camera-oriented Exif
tags or not. For example, JPEGs, PNGs, GIFs, BMPs, TIFFs, and more all qualify.
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 (with some
exceptions: see the HEIC update ahead), 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:
- thumbspage broadly supports JPEG, PNG, GIF, and BMP images across
all commonly used browsers.
- Support for TIFF, WebP, TGA, PBM, and rarer image
types is more limited and entirely browser dependent.
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 macOS
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.
Update:
in 2021, support for Apple's HEIF/HEIC image format common on iPhones is still not present
in either stock Pillow or Python's mimetypes module (the latter means it won't
appear in galleries at all), but it might be usable in thumbspage with a third-party library and
programming solutions.
Barring such extensions, your best bets may be to convert these images to JPEGs,
or change your Camera or Photos settings to save or transfer in JPEG format, respectively.
Try searches
here and
here for pointers.
- Other image-folder content
-
Because images in the source folder are detected by their MIME types (per
the prior note), all non-image items in the source folder are simply ignored.
Hence, it's safe to include arbitrary files and folders in the images folder;
apart from the next note's special case, all unrelated items, including Unix
hidden files, are always skipped and ignored by thumbspage.
Update: beginning in 2.3, files in the images folder
with a .note extension are special, and provide image notes displayed in the UI
when present; see the 2.3 coverage. Technically, header
and footer files are similarly special.
- Subfolder bullet lists
-
As a special content case, subfolders in the images folder will show
up near the top of the index page in a
subfolder-links bullet
list, if the subfolders feature is enabled in the configurations
file.
This allows your pages to provide access to
nested content—including details about the
folder, or image folders within image
folders—if
useful in your galleries. For example, you might use this to describe a
gallery's images, instead of text in a custom header file.
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;
version 1.7's update to skip .* subfolders too;
and version 2.1's customizable space between subfolder-list items.
- Linking to results with URLs
-
When referencing this program's results in hyperlink URLs, note that
links to generated folders of forms
.../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
- Tilted-image rotation
-
As of version 1.6, any images that are
"tilted"
(stored by cameras and
smartphones with orientation tags, and content shifted to a side) are
by default automatically rotated to display right-side up—both the
image itself
and its
thumbnail.
This works for all images with usable
Exif orientation tags
(see the standard),
which generally includes all JPEG photos created by recent devices,
and as of version 1.7 propagates Exif metadata tags to the rotated
image.
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 and later updates to them in
Version History ahead for more background
(version 2.1, for example, adds deletion of embedded thumbnails on rotations).
- Cleaning the thumbs folder
-
thumbspage always regenerates viewer pages for each image on each
run, so they correctly reflect your image set. For speed, though,
image thumbnails created in prior runs are reused if present in the
thumbs subfolder: new versions are not created, even if images have
changed. Hence, you should always select the "Clean thumbs folder"
console option in reply #2 (see Running thumbspage
earlier) to force regeneration of thumbnails if any images have been changed
or removed.
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.
- Thumbs-folder name
-
The nested folder used to store generated thumbnails and viewer pages is
named
_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).
- Extra-files overhead
-
This script adds two files for every one source image (thumbnail
and viewer page). That's negligible in most use cases, but may
become significant in archives with very many files (10k images
means 20k extra files). Incremental backup tools like
Mergeall,
for instance, may need to analyze and skip all files added.
If this impacts your usage, thumbs folders can be zipped into
single file archives where appropriate and needed.
- A word on image size and speed
-
The trade-off between image quality and download speed is a classic
dilemma on the web, and remains a peripheral factor when using
image-focused programs like thumbspage. While this factor is not
unique to thumbspage, and applies only to galleries published live
on the web, this note provides a few tips on the subject.
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 better world, online images would by now be immune to the scourges of
metered access and traffic bottlenecks. In the world in which we code,
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 VPS
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.
GUI Mode
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 (macOS and Linux), and probably on
Android (using a launcher script in the Pydroid 3
app).
To start, pass it any image folder's pathname as a command-line argument,
as follows:
/.../thumbspage$ python3 viewer_thumbs.py examples/unicode/images # Unix
c:\...\thumbspage> py -3.3 viewer_thumbs.py examples\unicode\images # Windows
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 both remote and
same-device viewing; your page and browser become the GUI and its host.
Update: running the GUI-viewer command lines above
requires the full examples/ folder, which may not be included in
your thumbspage download package (it may be better accessed online as of version
2.1 due to its
size). See the main
web page
to fetch this folder if desired and required, or use the pathname of a different
image folder of your own.
Update:
as of 2.1, the GUI viewer always stores its thumbnails in a subfolder named by
the THUMBS setting in the configs file,
which is also used by the HTML gallery builder, and is preset to _thumbspage.
This ensures that the viewer won't make a separate thumbnails folder, unless you change
the setting between gallery and viewer runs. If changed, the viewer's thumbs folder
may be redundant, but won't appear in gallery index pages as long as THUMBS
starts with a leading _ or . (the viewer's former default did not).
More on subfolder lists here.
Usage Caution
Finally, a word from the legal department.
thumbspage has been tested extensively and used successfully on a wide variety
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:
Please note:
by design, this program will modify your images folder
in-place, and may modify some of its images after backups.
To the folder, it
adds
an HTML index-page file (named index.html by default), along with a
subfolder (named _thumbspage by default) containing thumbnails and
HTML viewer pages. As preconfigured, it also
rotates any tilted images and
deletes their embedded
thumbnails, after saving unmodified backup copies
with .original extensions. Run this program on folder
copies if you don't want it to change your valued photo collections
directly, and read this guide for full program-usage details.
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-prerotate-originals.py;
and both image rotations and thumbnail deletions 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.
Version History
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:
- 3.0.1: Android Chrome Fix (2025)
- 3.0: Tags, Keys, Swipes (2025)
- 2.3: Notes, Inputs (2022+)
- 2.2: Swipes, Configs (2021)
- 2.1: Thumbs, Layout (2021)
- 2.0: Auto, Top, Full (2020)
- 1.7: Info, Exifs, Etc. (2020)
- 1.6: Scaling, Rotation (2018)
- 1.5: Viewer Pages (2018)
- 1.4: Mobile (2018)
- 1.3: Unicode (2016)
- 1.2: Styling (2016)
- 1.1: Subfolders (2016)
- 1.0: The Basics (2016)
3.0.1: Android Chrome Fix
thumbspage 3.0.1, published on August 12, 2025, is a minor patch
release with no new functionality and just a single fix that applies only
to recent versions of the Android Chrome (AC) browser.
That browser recently (somewhere around its version 135) began intercepting
and consuming down swipes on images in viewer
pages
for its pull-to-refresh action. Because down swipes are also used by viewer pages for image
info,
this AC change partly broke thumbspage galleries without any known warning.
Its CSS fix in the viewer-page template
file is simple:
body {
overscroll-behavior-y: none; /* disable pull-to-refresh in Android Chrome */
}
This code compensates for the fact that AC has apparently begun ignoring the
JavaScript preventDefault() in this touch-event context. It
should not impact either left/right-navigation swipes or other browsers in general,
and its impact on functionality is trivial: thumbspage's prior code already disabled
pull-to-refresh for down swipes on viewer-page images, though the fix extends this to
the entire page. See Chrome developer
docs
for more on the fix's basis.
It would be easier still to simply dismiss this as a quirk—or bug—of a
single web browser, but AC is 67% of the mobile market today per
statcounter and
statista,
so its churn can't be ignored. Browser share may naturally vary per gallery, but even
the 27% for AC and its Webview per analytics at thumbspage's hosting
site matters. Moreover, a
chrome://flags setting that formerly disabled down-swipe
interception was removed by AC, leaving no other workaround.
Such are the fruits of market dominance and constant churn.
3.0: Tags, Keys, Swipes
thumbspage 3.0, published on June 20, 2025, is a major release
with a host of new features. You can sample its mods interactively in its
live demo,
screenshots, and realistic
use case.
This section covers the following 3.0 changes:
Backward incompatibilities: 3.0 mods preset defaults
for some prior configurations in ways that may impact builds.
Dynamic layout is now the default for index pages, which means one
less console input may be asked, and a new dark mode is enabled as preset.
3.0 also recognizes HTML tags and entities escaped as
_<...> and _&...; in note text, and these may appear in prior notes.
Please adjust notes, configs, and
builds as needed.
Version 3.0 Change Details
The following major changes were made in 3.0:
- Omitted filename extensions
-
As of 3.0, setting new config
omitFilenameExtensions to
True omits filename extensions (e.g., ".jpg", ".png") on
both index and viewer pages when filenames are used as image labels.
This includes viewer-page HTML page titles.
Dependency: if config omitIndexPageLabels is True,
this setting has no effect on the index page (image labels are then omitted
in full), but it still causes extensions to be omitted in viewer pages.
Note that spaces and most other characters work in filenames too, so they can serve as general image captions. This is subject to the filename-character limits on the platforms that will host your gallery: for portability, your filenames generally shouldn't contain any of the characters / \ | < > ? * : ". Unix allows most characters except /, but this varies per filesystem and OS. - Gallery wraparound messages
-
If new config
useEndOfGalleryMessage is True,
a 2-second auto-closing message appears whenever moving to the
first or last image, during both Next/Prev manual
navigations and Auto slideshows of viewer pages.
These messages never appear on index pages.
3.0 adds two ways to automatically denote end-of-gallery
when navigating forward from the last image or backward from the first image.
These navigations, called wraparounds, can occur during both
Next/Prev manual requests (including their swipe gestures)
and Auto slideshows. useEndOfGalleryMessage emits
wraparound messages; the next note's useEndOfGalleryPage creates an
end-of-gallery page.
One, both, or neither can be enabled this and the following note's configs.
If neither is True, both Next/Prev and
Auto silently advance to the first/last image on wraparounds.
This is the prior behavior but may be subpar if users cannot tell that they've
wrapped around to the beginning or end of the gallery.
- Automatic end-of-gallery images
-
If new config
useEndOfGalleryPage is True,
a simple "End of Gallery" page is added to the end of the gallery.
This page appears in both Auto slideshows and
Next/Prev manual navigations of viewer pages,
but it does not appear on the index (thumbs) page.
useEndOfGalleryPage automates a technique some users were already
using, but the prior note's useEndOfGalleryMessage may be preferred
for galleries that already have an obvious beginning or ending because it
doesn't add a page. See the preceding note for more info.
- Raw-view messages on image taps
-
3.0 also shows a 2-second message on image clicks to announce
raw-view mode and instruct the user to tap Back to return to the gallery.
This is not configurable because it's always useful; else, users may not
realize that they've left the gallery due to an unintended image tap.
- HTML tags and entities in notes
-
As of 3.0, image notes may include embedded HTML tags and entities
that are passed along to browsers unchanged in viewer pages to be used
in rendering. These are coded with a leading underscore (
_)
to escape the tag or entity, and work for note text in both .note
files and the new NOTES.py dictionary.
Tags allow notes to use <a> for hyperlinks,
<b> and <i> for bold and
italics, <hr> for divider lines, <br>
for line breaks, <span> for colors and fonts,
<style> for embedding CSS rules in note text to be
referenced by other tags, and more.
Entities allow characters like spaces,
— dashes, and &#xxx;
Unicode and emoji codes. Most entity characters like emojis can also be coded
literally in notes, subject to note Unicode encoding and text editor support.
Examples: to embed a tag in a note, simply prefix its opening and closing parts
with an underscore:
We _<i>really_</i> like
_<a href="https://quixotely.com">this_</a> app
_<span style='color: cyan; font-weight: bold'>alot_</span>!
_<style>.wt {color: white}_</style>
Coding _<span class=wt>note_</span> text with _<span class=wt>style_</span>
To embed an entity in a note, prefix it with an underscore the same way:
_— _< _ _¥ _< _👍 _👍
All note characters outside _<...> and _&...;
sequences are rendered verbatim as before and not interpreted as HTML syntax.
Tags embedded in note text may span multiple lines freely (e.g.,
_<a attr=xxx\nattr=yyy\n>);
any embedded newlines are treated normally per the host's HTML processor.
Because notes become mini web pages with the addition of embedded tags,
they also now stay open for further reading until explicitly
closed—even after navigating to another image or the index, or
returning from a link embedded in the note via browser Back.
Else, reading more of the note may require manual reopens, subject to
browser back-forward caches. This normalizes page URLs so all variants
reopen a page's note (e.g., ' ' and '%20' match).
Info popups may not remain open on returns, but they have no embedded
links to follow.
See also the related popupLinksColor config
and its dark-mode counterpart for colorizing links
embedded in notes.
Cautions: any stray _<...> or _&...; in a note
is now interpreted as an HTML tag or entity, and HTML syntax errors in tags are
not handled gracefully. Preescape stray leading underscores with
__ as needed,
and code and test HTML components in notes carefully. For examples of
coding tags and entities, see the *.note and NOTES.py
files in the 3.0 demo.
- Explicit ordering with ORDER.txt
-
As of 3.0, the optional file
ORDER.txt provides an alternative and
explicit way to specify image-file order that overrides filename ordering.
If present in the images folder, this file lists one image filename per line
(unquoted, and omitting folder paths but including .xxx extensions).
Its top-to-bottom order is the order in which the gallery presents listed images
and is used for both the index page and viewer-page navigation. An ORDER.txt
example:
filenameZ.jpg
filenameY.png
filenameX.JPG
Any images not listed in ORDER.txt are still ordered by filename subject to
config caseSensOrder, and appear after all images ordered per ORDER.txt.
Note that this orders image files only; automatic subfolder links are always
alphabetical, subject to caseSensOrder.
Ordering always uses case-sensitive matching of filenames on all build hosts,
and this file's Unicode encoding is per config insertEncoding.
To catch spelling and other mistakes, a "*CAUTION" is printed for entries
in the ORDER.txt file that don't name a real image filename.
Tip: ORDER.txt is not required if image filenames have a natural order.
Years, camera-assigned numbers, etc., can make filenames sufficient. But
ORDERS.txt can be more convenient than numbering filenames manually and
doesn't rely on photo Exif tags, which unreliable or unavailable in some contexts.
See also the ordering file of the 3.0 demo.
- Alternative labels with CAPTIONS.py
-
For an optional and advanced way to provide image-label captions, use
3.0's
CAPTIONS.py file option. If present in the images folder, this file
contains a Python dictionary that associates any number of image filenames
with caption text strings using syntax {'filename.xxx': 'caption text', ...}.
Non-string values in this dictionary are automatically converted to strings,
and any newlines (\n) are ignored.
Here is a CAPTIONS.py example (Python dictionaries have syntax
similar to JSON text):
{
'filename1.jpg': 'Building on Android \n is \n 😱🤪?',
'filename2.png': 'Anything goes: / \ | < > ? * : " 👏',
'filename3.JPG': 63,
}
Any captions coded in CAPTIONS.py override filenames and appear as labels
on both index and viewer pages. They may contain any Unicode text and are not
subject to platform character limits (see above). If caption
text spans multiple lines with """...""", its embedded newlines are ignored
by the host browser.
Captions in CAPTIONS.py are not shortened by
omitFilenameExtensions, but are omitted from index pages if config
omitIndexPageLabels is True.
CAPTIONS.py should be encoded (saved) per Unicode's general UTF-8
encoding; all ASCII text and files automatically qualify.
Captions always use case-sensitive matching of filenames on all build hosts.
To catch spelling and other mistakes, a "*CAUTION" is printed for entries
in the CAPTIONS.py dictionary that don't name a real image filename.
See also the captions file of the 3.0 demo.
- Alternative notes with NOTES.py
-
As of 3.0, the
NOTES.py file provides an optional and advanced
way to code notes. If present in the images folder, this file contains a
Python dictionary that associates any number of image filenames with note
text strings using syntax {'filename.xxx': 'note text', ...}.
Use normal Python rules to code multiline note text in this scheme:
"""...""" blocks, line\ continuations, and
'' '' auto concatenation.
Here is an example NOTES.py, indented for clarity (Python
dictionaries use syntax similar to JSON text):
{
'filename1.png': 'A simple single-line note.',
'filename2.jpg':
"""
A multiline and/or multiple paragraph note goes here.
The _<B>best_</B> book_<I>you'll_</I> find at
_<A href='https://learning-python.com/about-lp6e.html'>amazon_</A>!
""",
'filename3.JPG':
'A multiline note '
'with two emojis: 👍 _⭐️',
}
NOTES.py is an alternative to individual ".note" files but works the same
way, and augments and overrides any ".note" files also present in the images
folder. NOTES.py uses Unicode's UTF-8 encoding; all ASCII text qualifies.
Notes always use case-sensitive matching of filenames on all build hosts.
To catch spelling and other mistakes, a "*CAUTION" is printed for both
".note" files in the images folder and entries in the NOTES.py dictionary
that don't name a real image filename.
See also the notes file of the 3.0 demo.
- Per-host or forced dark color theme
-
If new config
useCannedDarkTheme is not False,
the CSS style themes in this program's template-autothemes.html
are inserted into both index and viewer pages, and a separate set of color
configs are applied in dark mode.
This is primarily meant to support a dark-mode theme that can be responsive
to display settings on the host device, and overrides other color settings if used.
Please note that it is also provisional and somewhat experimental in
3.0, and it may not work with all custom headers and footers for index pages.
Comments on it are welcome and will help guide future dark-mode improvements.
Set useCannedDarkTheme to one of:
False to disable this feature in full
'host' to use dark theme only if dark mode is enabled on the host device (preset)
'always' to force the dark theme irrespective of the host device's mode
This config is preset to 'host', which means enabling and disabling dark mode
on the host device will toggle between dark theme and all other color configs in this file.
Dark theme (mode) is implemented with both a new template file and new color configs, described
in the following coverage.
Template file:
dark theme is partly defined in program file template-autothemes.html. Edit
as desired, though its preset defaults can be accepted verbatim. This file's style
code is applied to both index and viewer pages, but as shipped, defines
only a dark theme. In index pages, this dark theme is defined after a custom
HEADER.html file but before a FOOTER.html, so it
overrides the former but not the latter. In viewer pages, its location is irrelevant.
For always-light or always-dark themes, simply tailor the main color
settings in user_configs.py.
The new dark theme instead supports colorizations that are based on, and
respond to changes in, the host device's light/dark display mode.
It's somewhat at odds with this program's original color-settings model, and
viewer pages have always been dark to avoid clashing with images, but the new
template is required to override styles in custom headers for index pages.
Color configs: there are also new settings in
user_configs.py
that give colors used in dark mode only, whether it's per host or forced.
All of these start with prefix darkTheme to make them distinct
from their original counterparts, which are now used in both light mode and
when dark mode is disabled. These configs may avoid changes to the dark
template file template-autothemes.html and are easier to edit
for builders with limited CSS experience.
Among the new dark-only color settings, darkThemeLinksColor can
be used to set the color of all viewer-page links per gallery. It colorizes
both toolbar links on viewer pages and embedded <a> links
in note text. Use any CSS color specifier; it's preset to
popupLinksColor (below),
used for embedded links when dark theme is off.
In addition, there are new darkThemePopup* color settings in
user_configs.py that control the text and background
colors of all popups in dark mode only—notes, info, and messages—as well
as other new darkTheme* configs that are applied to dark-mode pages
the same as their original/light counterparts. See the configs file for more details.
- Configurable line spacing for text
-
If new config
textLineSpacing is not None,
its value is used to set line spacing for all text in the index page
(including text in custom HEADER.html files), as well
as image info and note text in viewer pages. Any CSS size value may be used (e.g.,
'1.3em', '150%', '24px') and is applied to
the CSS line-height attribute. A value of None
(no quotes) uses host browsers' default spacing, as before.
Extra line spacing may aid readability, especially in dark-mode themes, and
can make embedded links easier to tap on mobile devices and PC touch screens.
- Image filenames in info popups
-
Because file extensions may now be
omitted
and filenames may be replaced with arbitrary
captions,
the filename of an image being displayed now appears at the
top of the image-info popup issued on label taps, down swipes, and
? keypresses. See the example
here.
- Swipes on touchpads and mice too
-
In 3.0, viewer pages now respond to left/right and up/down swipe gestures on
touchpads and mousewheels to trigger
Prev, Next,
Note, and image info. This is in addition to the prior touchscreen
gesture support.
The new swipes are generally triggered by a two-finger swipe on a capable touchpad
or a mousewheel spin (which may be a wheel tip, one-finger swipe on Apple "magic" mice,
or other gesture). On some devices, sensitivity may need to be scaled down for better
responsiveness.
Because the new support will prevent pad/wheel swipes from reaching the browser,
it can be turned off per gallery build by new config doTouchpadMouseSwipe.
Though they work, pad/wheel swipes are also laggy compared to touchscreens because they
use a mousewheel event that must be debounced until the wheel event stream is over.
Tip: on PCs, use touchscreens (where available) or keypresses (up next) for
faster response than touchpad swipes or mousewheel spins.
- Keypress shortcuts in viewer pages
-
In addition to swipes and widget taps. viewer pages now respond to a
set of keyboard key presses. If a keyboard is present on the host
device, the keyboard keys on the left of "=>" in the following are
the same as tapping widgets in the right and automatically invoke
the action in parentheses:
p => Prev (previous image)
n => Next (next image)
i => Index (return to index page)
t => Note (open image note)
a => Auto (toggle slideshow on/off)
f => Full (toggle fullscreen on/off)
? => label (open image info)
. => image (open raw view)
Enter => OK (close an open dialog)
There are no configs for this extension because it has no known downsides.
This raises the number of UI options for viewer pages to four: widget taps
or clicks, touch screen swipes, touchpad/mousewheel swipes, and keypresses.
- Error checks for filename references
-
To catch spelling and other mistakes, a "*CAUTION" is printed
for filenames in build files that do not reference a real file
in the images folder. This includes the names of
.note
files, as well as the contents of ORDER.txt,
NOTES.py, and CAPTIONS.py described above.
- Preset defaults and links color
-
Dynamic (a.k.a. responsive) index-page layout,
useDynamicIndexLayout,
is now the default, because it both has been battle tested and is generally better
across a range of devices. This may break build scripts that use precoded console
input because number-columns-per-row is not asked for dynamic mode; mod
as desired. There is also a new popupLinksColor config to
colorize hyperlinks nested in <a> tags in note text,
subject to dark-theme overrides above.
Please see user_configs.py
for these and other config preset changes in 3.0.
2.3: Notes, Inputs
thumbspage 2.3, published initially on February 8, 2022, includes three major
new features, plus smaller upgrades. This section provides overviews
of its new image notes,
console-input overrides,
enhanced tooltips,
and other changes.
You can also find a demo of image notes and tooltips in the
2.3 examples folder.
Updates: 2.3 was later repackaged:
-
On May 9, 2022 with
code changes to support custom
colors and
opacity
in info and Note viewer-page popups;
improved dynamic layout for index pages;
omitted filename labels on index pages;
fixes for three minor buglets;
and a small builders-UI enhancement.
This was a major upgrade, but part of the 2.3 project.
-
On November 21, 2023 with a trivial patch to avoid problems caused
by Python's opinionated removal of the
cgi standard-library module
(despite the module's three-decade tenure and millions of clients).
Without the patch, this triggers warnings in Pythons 3.11 and 3.12,
and aborts in 3.13 and later.
A handful of typos were also fixed in docs,
but no program behavior was changed.
-
On December 21, 2023 with a trivial patch to avoid problems caused
by Python's opinionated breakage of string escapes (which have worked well
for thirty years and been used by millions of programmers).
A
\ followed by a non-special character is no longer
allowed; doesn't retain the \; issues a warning in Python 3.12;
and may be an error in 3.13. The trivial fix is to use forms
r'' or \\. No program behavior was changed by the fix.
Image Notes
With 2.3, galleries can optionally include notes for images,
which are displayed as
popups
in viewer pages when requested by either taps on Note
buttons
or up swipes on touch devices.
This provides a way to describe or narrate any or all of the
images in your galleries, and opens up an new presentation
dimension for both builders and users.
This section covers this feature in full, but if you wish
to test-drive notes up front, visit the 2.3 upgrades
example, as well
as the online training-pictures
gallery.
Other examples do not yet have notes, because this wasn't available until
2.3; galleries that predate 2.3 were regenerated with 2.3,
but those without notes work as they did before.
You can also read the users perspective in the viewing section
here and here,
and the parallel coverages in the customizations
section and the configs
file.
How Notes Work
In more detail, image-viewer pages may now include a Note toolbar button,
which displays the plain text of an imagefilename.note
file near the bottom of the page, if such a file is present in the images
folder at gallery build time. (Update: as of
3.0, notes can also be coded with a Python dictionary in
a NOTES.py file in the images folder. If used, this file's
notes augment and override individual note text files.)
This feature is enabled by coding one or more note files. It
can be disabled by the config file/argument setting
useImageNotes, and is automatically disabled if no
.note files exist for any images in the folder (this avoids
showing Note buttons on viewer pages which would all be no-ops).
When notes are enabled, up swipe on the image on touch displays is
also changed to open the note, instead of raw-image view. This is symmetric
with down swipes that open the info popup at top of page. Raw view is
still available via a simple image tap when notes are used, but the
2.2 upSwipeOnAllBrowsers setting is ignored in this case, because
up swipe opens the Note popup instead, and works on all browsers;
unlike raw view, no page switch is required for notes.
More formally, image notes is a backward-compatible extension, with
behavior that works as follows:
If useImageNotes is False OR no images have .note files:
- No Note buttons are displayed in viewer-page toolbars
- Any .note files in the images folder are ignored
- Up swipes work as they did in 2.2, opening raw-image views
If useImageNotes is True AND any image has a .note file:
For images with a .note file:
- The viewer page shows a Note button with normal font and color
- Note taps pop up the .note file's content as plain text
- Up swipes also open the note file's content, not raw-image views
For images without a .note file:
- The viewer page still shows a Note button, but with line-through
- Note taps and up swipes work, but show default text "(No note)"
In other words, viewer pages are the same as they were in 2.2 if no note
files are present, but gain Note buttons and redefine up swipe if notes
are enabled. Because useImageNotes is preset to True in the
configs file, notes are by default enabled and disabled by the presence
and absence of .note files in the images folder, respectively.
The setting provides an extra level of control when needed.
Two fine points: first, notes are displayed during Full one-page fullscreens,
but do not stay open during Auto slideshows, because images would be both
shaded and obscured; tap Auto to stop the show if you wish to read a Note.
Second, .note files, like custom header and footer
files, need not
be present when a gallery is viewed; because .note files' text
is embedded in generated viewer pages, these files are required at build
time only.
Coding Notes
To code a note for an image, simply create a plain-text file with a
.note extension in the images folder, alongside the image
itself. For example, make text file images/photo.jpg.note
to give the Note text for image file images/photo.jpg.
Here's a typical note file from 2.3's
demo.
You can create notes for any, all, or none of your images; images without
an associated note display Note buttons with a
line-through and display
default note
text,
and no Note buttons appear if there are no note
files.
In terms of content, any Unicode character can appear in a note file, including
emojis.
In addition, characters special to JavaScript or HTML are automatically escaped
and render verbatim. ' and \, for instance, appear
literally and do not impact note text, and <A> displays as is
rather than producing a hyperlink (notes are plain text by design, for simplicity).
For a demo of special-character escapes, see the note
here.
(Update: as of 3.0, HTML tags and
entities escaped with a leading underscore are now passed on to browsers instead
of being rendered verbatim. This allows links, italics, and more.)
Note content loaded from .note files is decoded per
the Unicode config
setting noteEncoding,
which is preset to UTF-8. You can use any
text editor to create note files, but be sure that files are saved in a
Unicode encoding compatible with this setting if non-ASCII characters
are present.
When note-file contents are displayed, the viewer-page popup collapses all
text into a single paragraph, except that an empty line coded as two adjacent
line breaks in the note (i.e., \n\n in Python, with no spaces
between), is treated as a
paragraph break in the popup, and renders as a blank line.
You can use this to code as many paragraphs as you like,
though as a guideline, notes should be relatively short, especially
for use on mobile (a picture is, after all, already worth 1k words).
The Note popup itself expands and shrinks with the window, and its text
scrolls vertically if too tall for the page. If desired, the width of the
popup can be adjusted by the config file/argument setting
noteBoxVSpace, which gives the CSS size for both left and right
margins, and can be any size type (e.g., 10% or 5px).
The preset 15% makes the popup span 70% of the page, and reflects
a compromise between mobile (where wider is better) and desktop (where the
popup tracks the width of the window).
Tip: if your gallery includes images notes, filename labels
might be superfluous on its index page. See the thumbs-only index
page option ahead to omit these labels
if they don't add any value to your gallery.
Batching Notes
One potential downside of using individual .note files
is that they are somewhat scattered, making it difficult to treat
them as a set (e.g., to spellcheck all notes before publishing a gallery).
To address this, 2.3 also includes a utility script,
batchnotes.py, which
collects all of an image folder's note files into a single text file,
with separator lines and sorted by filename. The result can be easily
pasted into a spellchecker, or otherwise managed.
See the script for usage, and see its output for the 2.3 upgrades
example, as well
as the online training-pictures
demo.
In principle, this script's output could be split into individual note
files (and hence provide an alternative way to code them), but no use
case for this has arisen thus far.
Colorizing Notes (and Info)
Like
info popups,
2.3's
Note popups
initially inherited the color scheme of viewer pages at large,
whose preset was white text and border on black. This generally makes sense
because additional colors can clash with gallery images, but some colors may make
text difficult to read on some displays, especially for larger notes. Bright
white text on full black, for instance, may be too glaring for some readers.
To compensate, 2.3's latest May 2022 release allows builders to customize the
background, foreground text, and border colors of both Note and info popups. Any or
all of these three colors may now differ arbitrarily from those of the viewer pages
in which the popups appear.
To tailor these popups' colors, assign the new popup*Color settings
in either the configs file or its command-argument
equivalents. Their preset defaults of
None mean the settings inherit their values from the enclosing viewer
page as before, for backward compatibility (technically, None selects
the corresponding viewer*Color setting). If any of the three new
settings are not None, their values are applied to popup dialogs.
The following build commands, for example, may improve text readability by specifying
muted white text to minimize glare on the default black background; off-white text on an
off-black background; and a black-on-grey popup scheme:
$ py3 .../thumbspage.py images/ popupFgColor=\'#dddddd\'
$ py3 .../thumbspage.py images/ popupBgColor=\'#181818\' popupFgColor=\'#cccccc\'
$ py3 .../thumbspage.py images/ popupBgColor=\'darkgrey\' popupFgColor=\'black\' popupBorderColor=\'white\'
In these settings' names, Fg means foreground text, Bg
is the dialog's background, and Border denotes borders around
both the dialog and its OK button.
When used, these new popup colors are applied to both Note and
info popups, but
not to viewer pages at large, whose toolbars, filenames, and image borders
still follow viewer*Color viewer-page settings.
While graphic arts is a topic beyond this doc's scope, popup colors
as a rule should blend well with the viewer page. In accommodation,
popup OK buttons' background and foreground colors by design
still match the page, not the popup; only their borders respond to the new
popup color settings.
To sample the effects of these new configs, see the 2.3 gallery captures starting
here,
as well as the training-photos
gallery.
The former's notes give additional tips and command examples, and
the latter's
generate and
publish scripts
use command forms like those above to make popup text more readable.
As part of this upgrade, OK buttons also morphed in appearance slightly,
and gained rounded corners to match the enclosing popup box; this is a
visual change, but the mod may vary by browser, and is too minor to
warrant fanfare (or a new version number).
Console-Input Overrides
With 2.3, all console inputs can now be provided with config-file
or config-argument settings, instead of actual inputs. This resolves
an inconsistency that arose in
version 2.2: config command-line
arguments were allowed to override settings in the configs
file, but a disjoint set of parameters
was still always input at the console (technically, from stdin).
As of 2.3, these inputs can now be provided by either config-file or
config-argument settings, but are still asked by default so existing builds
are not affected. The net result provides a portable and uniform
alternative to shell input techniques, which is backward compatible
with earlier releases.
Input-Override Configs
In more-concrete terms, the following new config settings correspond to
inputs requested interactively at the console in all prior releases:
inputImagesFolderPath = None # string 'path' (iff not first argument)
inputCleanThumbsFolder = None # True or False
inputThumbsPerRow = None # integer (iff not dynamic layout)
inputThumbMaxSize = None # 2-tuple of ints (or int x == x,x)
inputUseViewerPages = None # True or False
Of these, inputImagesFolderPath is never asked (and is
ignored) if the images folder path is provided in the command's first
argument
(per 1.7);
and inputThumbsPerRow is never asked (and is
ignored) if dynamic index-page layout is used
(per 2.1).
Any or all of these settings may be provided in the config file itself,
or as setting=value command-line arguments,
which were added in 2.2 and take precedence
over the file's settings. However provided, these settings may
override inputs as follows:
- If the setting is
None, the corresponding input is requested
in the console as before.
- If the setting is not
None, its value is used for the
corresponding input, and the input is not requested in the console.
For backward compatibility, all input-override settings are preset to
None in the config file, to ask by default. Hence, this requires no
changes in existing build systems that predate 2.3. When used, however,
input-override configs make command recall more useful, and can replace
shell < inputs redirects and <<EOF here
documents discussed earlier,
which are not generally portable across platforms and shells.
About precedence: with this change, build parameters may be provided in three
different forms. When multiple setting forms are used, command-line
arguments have the highest precedence, followed by the config file's
variables, and then console inputs. That is, arguments are used first
and inputs are tried last, and inputs are not requested if overridden by
either argument or file. Special case: for the image-folder name, the
first command-line argument, if provided, has higher precedence than
all three other parameter sources.
Input-Override Examples
With the addition of input-override configs, thumbspage build commands are both
flexible and complex. As a tutorial, the following provides examples of
common command structures, assuming C is a shell variable
set to the folder where you unzipped thumbspage's source-code (extrapolate
as needed on non-Unix
platforms):
Folder by first arg, dynamic layout, inputs by config file or console
$ python3 $C/thumbspage/thumbspage.py . useDynamicIndexLayout=True
Folder by first arg, dynamic layout, all inputs by args
$ python3 $C/thumbspage/thumbspage.py . \
useDynamicIndexLayout=True \
inputCleanThumbsFolder=True inputThumbMaxSize=128,128 inputUseViewerPages=True
Folder by first arg, fixed layout, all inputs by args
$ python3 $C/thumbspage/thumbspage.py trnpix \
inputCleanThumbsFolder=True inputThumbsPerRow=4 \
inputThumbMaxSize=100,100 inputUseViewerPages=True
Folder by config arg, fixed layout, others by config file or console
$ python3 $C/thumbspage/thumbspage.py inputImagesFolderPath=\'.\'
Folder and all others by config file or console
$ python3 $C/thumbspage/thumbspage.py
As a guideline, use the config file for settings that are defaults
or universal, and either arguments or console inputs for settings that may
vary from build to build. Console inputs can still be used by build scripts
coded to provide them before 2.3, but their config overrides
are handy for launching a build quickly with shell command recall,
and are a natural extension to the config-argument model.
Nit: inputs provided by config file or argument are not error checked as much
as actual console inputs. Configs are checked for valid Python syntax, but
may yield invalid values or types which won't generally cause harm but may
trigger exceptions and aborts during a build. thumbspage assumes
that builders clever enough to edit the config file or pass command arguments
are also clever enough to provide valid inputs. Still, lapses aren't out of
scope for even the best of us; builders beware!
Tooltips: Enhanced, Enabled
With 2.3,
tooltips
are now enabled by preset default, because
viewer-page toolbars are now busier when Note is used. In addition,
tooltips have been added for the Prev, Next, and Index
buttons for
consistency, and the former HTML hrefs used as a fallback for navigation
buttons have been removed, because the mouseover URL popups they invoked
are redundant with the buttons' now-default tooltips, and were noticeably
more obtrusive. As before, tooltips do not work on mobile browsers,
even with a stylus or mouse.
Caveat: the removal of hrefs now makes Prev, Next, and Index
unusable without JavaScript in 2.3, because the hrefs formerly
provided a fallback when JavaScript was disabled. This is a
backward-incompatible change, but given that viewer
pages were almost completely unusable without JavaScript already
(e.g., images were not sized to the display, and other widgets did
not work), this seems a small cost to pay for avoiding
the former and distracting mouseover URL popups. It does,
however, leave no usable widgets without scripting.
Hence, thumbspage viewer pages now officially require JavaScript.
This was previously resisted, but JavaScript is now generally mandatory
on the web at large, and is a necessary and reasonable tradeoff for enhanced
utility of the sort provided by viewer pages.
As before, a message
is posted at top of page when JavaScript is
disabled, to let users know that pages are not operative. The
Note, Auto, and Full buttons are also omitted in this case,
as another visual (if perhaps inconsistent) clue; Prev, Next,
and Index remain for show and layout, but are no-ops sans JavaScript.
Other 2.3 Upgrades
The usual basket of smaller enhancements found its way into 2.3:
- Popup rounded corners
-
Viewer-pages' former info popups now use rounded corners for
style,
and the new Note popups follow
suit. It's what
we do in 2022, though images are still square to imitate paper photos
and avoid cropping detail. To match, OK buttons
within info and Note popup dialogs
are now rounded
too
(as understated above).
- Popup opacity config
-
The background dimness for info and note popups
can now be customized per build by setting
popupOpacity in the config
file or a command
argument.
This new setting gives the opacity of the
page overlay: a higher value makes the background dimmer (darker),
and a lower value makes the background brighter (lighter).
Strictly speaking, values may range from 0.0 (transparent) to 1.0 (full blackout).
The new config's preset 0.40 (40%) matches prior
versions and is a fair compromise, but brighter may be better when notes
refer to images (so images can be better seen), and darker may be better for long
notes or bright images (so notes get more focus and images don't outshout note text).
For a brief demo, see the screenshots starting
here.
- Simpler gallery builds
-
Non-trivial gallery builds are much simpler now, with 2.2 config
arguments
and 2.3 input
overrides,
instead of the former
sed config-file edits and
<<-EOF here documents.
Most examples' build scripts don't use the new tools yet,
but one shows how,
and another
does.
The online training gallery's
generate and
publish scripts
provide another example
of the simpler new-style builds.
- Simpler thumbs-max-size input
-
The maximum-size build parameter for thumbnails can now be given as a single integer
instead of a tuple of two integers, though only when it is provided in the configs
file or its
inputThumbMaxSize
command-argument equivalent. This value must still
be a 2-tuple when input from the console. When given as an integer value X
in configs, however, it is automatically treated as a 2-tuple (X, X),
with the same value for pixel width and height (i.e., with square bounds).
Because this is the normal case,
the new single-value config provides a convenient shorthand.
- Improved dynamic layout
-
The dynamic index-page layout option added in
2.1 now does a better job of arranging
thumbnails horizontally. In brief, the CSS code generated for the
thumbnails table now sizes cells using .5 'em' instead of 1 'ch' per
character of the longest label. This is irrelevant when images are
wider than labels (or labels are omitted per the next note),
but packs columns tighter when labels are wider than images. On larger candy-bar
mobiles, the net effect allows some galleries to display more than one
column,
while also avoiding fixed layout's horizontal scrolls.
For the full story on this change, see the
2.3 update in generateDynamicThumbsLayout() of file
thumbspage.py,
as well as screenshots starting
here.
As before, configs can be used to fine-tune
cell layout out as desired: see the
file and
example.
This largely removes the "experimental" status of dynamic
layout—enough so that the training-gallery
demo
now comes in a dynamic
flavor.
Despite its mobile scrolls, though, fixed is still the default, because dynamic
may still show a single column on some phones and leave empty right-side space
in some windows.
Foldable mobiles might render some such
tradeoffs moot, but it could be a long wait.
- Thumbs-only index pages
-
Per user request, the filename labels that normally appear
under thumbnails can now be omitted on index
pages.
This mode works for both fixed and dynamic index layouts, and displays just
thumbnail images. To sample its effect, see the dynamic
and fixed screenshots starting
here,
or the live demo.
Enable this option with new config omitIndexPageLabels,
and tweak its cell spacing in dynamic layout with configs
dynamicLayoutPaddingH/V per the config file's
docs; fixed layout sets spacing automatically.
Enabled or not, images are still sorted by filename for sequencing, and
filenames are still displayed at the top of viewer
pages for reference.
Thumbs-only index pages may be useful for galleries where thumbnail images suffice;
filenames aren't descriptive or useful; space is a premium for
mobile;
or 2.3's image notes make filenames extraneous.
Filenames generated by cameras, for example,
might be pointless, especially if notes provide photo descriptions.
On the other hand, this partly upends the
paradigm of presenting a folder of images (thumbspage began as
an alternative to browser views of image folders, and later, images themselves);
and image notes may be less convenient (notes can say more, but informational
filenames may be easier). As usual, your gallery is yours to craft.
- Three buglet fixes
-
Three minor defects are fixed in the latest 2.3, the first
two of which relate to the new image-notes feature, and the
last of which has been lurking since day one:
-
All
\ characters in .note files are now properly escaped
so they appear in viewer pages' Note popups. This cropped up only for notes containing
backslash characters (open the popups in this
example and
demo).
With the repair, \ and all other characters special to JavaScript and HTML
display verbatim and don't derail pages.
-
Uncaught exception messages no longer show up in the JavaScript
console for viewer pages of note-less galleries. These messages were harmless: they
were unseen by normal visitors, and did not impact gallery usage in any way.
The undefined-name coding issue they reflected has nevertheless been repaired.
-
Viewer pages' Index buttons now use the index-page name provided in the
INDEX config, instead of hardcoding index.html.
This config can vary per build and has been present since thumbspage's
origin in 2016, but was never utilized in six years. The hardcoding
came to light when the training
gallery
added a second index page for
dynamic layout:
without using the config, Index taps in the dynamic
variant's viewer pages incorrectly returned to the fixed variant's index.
Atypical but true.
Bonus: info popups now use the first fix's code to escape their device/software
name
too. Special characters were never known to be an issue for info,
but arbitrary text can be, well, arbitrary.
- TBD: touch pads
-
Some thought was also given to extending swipe gestures to
touch pads (in addition to touch screens), but browser support
seems too tenuous today. Perhaps tomorrow.
(Update: swipes for touchpads and mousewheels
were was added in version 3.0.)
2.2: Swipes, Configs
thumbspage 2.2, published on December 8, 2021, includes three major
new features, plus smaller upgrades. This section provides overviews
of its new swipe gestures,
command-line config arguments,
optional tooltips,
and other changes.
You can also find a demo of config arguments and galleries of tooltip
screenshots in the 2.2 examples folder.
Swipes Gestures for Touch
Version 2.2 adds support for swipe gestures on touch displays.
This works in all dozen browsers tested on both smartphones and
Windows PCs, except for Internet Explorer.
Where supported, swipes originating in the image-display
area of viewer pages move to next and previous images (for left and
right swipes);
open the image-info popup (for down swipes);
and open image raw view (for up swipes, where supported per ahead).
Swipe usage is described in more detail earlier in the viewing
section.
These gestures are alternatives to existing tap actions—specifically, taps
on Prev and Next buttons, filename widgets, and images—and the former taps
(and their mouse-click equivalents) still work as before on all platforms.
These gestures also do not preclude other touch gestures: long presses,
pinch/spread zooms, and screen-edge back/forward swipes still
work where supported earlier.
Because the direction of left/right gallery movement is somewhat arbitrary
(should it reflect Prev/Next toolbar button order or not?), this is
changeable in the configs file and its 2.2
arguments (per the next section). Namely:
if the new config setting lrSwipesPerButtons is True,
left/right swipes move to previous/next images, respectively; if False,
left/right swipes move to next/previous images instead. Because the latter is more
common and "natural," it is also the default preset.
Fallback: thumbspage's swipes play well with default actions in all browsers
tested, and are too useful to relegate to optional feature.
If they ever prove to conflict with other gestures important to your use
cases, however, you can disable viewer-page touch gestures completely by
simply commenting out the JavaScript
template
code that registers touch handlers; search for "DOMContentLoaded" in the template.
To sample swipes live, use a touch-screen device to open any of the demos listed
here.
Update: thumbspage 2.3 changes up swipes to open
image notes instead of raw views, when its new Note feature is enabled. This
up swipe parallels down swipes for info popups. Unlike raw views, this also works
on all browsers, because it does not require a page switch. Hence, the convolution
of up swipes—and the following notebox—go away completely in 2.3 when notes
are enabled. When notes are not used, however, up swipe is still raw view as it was
before, and comes with all the same barbs.
See the 2.3 release note for more details.
Update: thumbspage 3.0 extends swipe gestures to
also work on touchpads and mousewheels, in addition to 2.2's touchscreen support.
See 3.0's release notes.
Caveat: Up-Swipe Limitations
This note reflects the most-recent versions of browsers in late 2021, including
Chrome 96 and Firefox 94.
Due to browser glitches, thumbspage's up swipe to raw views is currently enabled only
for Firefox on all platforms, all browsers on iOS, and Samsung Browser on Android.
Other touch-device browsers, including Chrome and its Opera and Edge derivatives,
don't support this gesture's action, and respond to the gesture with a popup
message
to let the user know that the swipe was recognized.
The glitch:
in Chrome and its ilk, switching to the raw image's URL in touch events frequently
fails to push the calling page onto the history stack, despite using
code identical
to that which works well for image taps. The net result is that a later Back
does not return to the viewer page, which is both confusing and frustrating.
This
message
from Samsung Browser seems germane, though this browser allows Back to return
to viewer pages, and users are not returning to a page they did not visit.
This is clearly a misclassification; enabling popups and redirects in Chrome
does not fix this bug, and would be far too much to ask of users if it did.
The scope: this Back-after-swipe glitch occurs in Chrome and browsers
that share its code base (Edge and Opera) on Android and Windows.
It does not occur in Firefox on either platform, the Samsung browser on Android,
or any browsers on iOS (which all share the same code base per Apple edict).
Internet Explorer and other platforms are moot because they
don't support thumbspage touch events (IE implements a different
pointer-event API, and has been rendered nearly unusable on Windows as of late 2021).
The work-around:
rather than penalizing all browsers for a Chrome issue, up swipe has been
coded to work on browsers known to support its action today, but may have to be
revised if Chrome's bug is ever repaired. In the interim, if a fix appears in
Chrome before thumbspage can be rereleased for it, you can set
upSwipeOnAllBrowsers in the configs file
(or its arguments, up next) to enable up swipe for all browsers.
Setting this to True allows you to both test for a
fix, and activate support in galleries.
For now, raw views are still available on all browsers via image taps,
which work flawlessly, and are arguably easier than swiping the same widget anyhow.
Sports fans: up swipe was also tried as an equivalent to Index (but Back again
failed to return to the gallery); as well as Auto and Full to avoid
a page switch (but Auto was too subtle, and Full didn't
work most of the time—and was much too jarring when it did).
Fix please, Chrome.
Update: this issue is entirely moot in 2.3 when
notes are used: see the update above.
Command-line Config Arguments
Version 2.2 allows any number of config-file settings to be passed in as
command-line arguments. When used, these arguments override same-named settings
in the config file, and are generally more
convenient than config-file edits for customizations that vary per gallery.
Config-file arguments are coded in commands used to run thumbspage builds,
provide both a setting name and value separated by =,
and appear after the folder name if one is used:
python3 thumbspage.py imagefolder? setting=value setting=value...
In this, setting is the name of any variable assigned in
the config file; value
is any Python expression whose result is used for the config setting;
and there should be no spaces around the = characters.
As a Python expression, the value part should use
quotes around strings and escape them as needed (e.g.,
\' or \"), and any other characters special
to your shell should be similarly escaped as required (e.g., \&).
Windows escaping rules naturally
differ,
unless you're using its Linux
subsystem.
Config arguments with invalid names or values that produce
exceptions on evaluation cause the gallery build to be aborted,
with a message in the console.
In the past, changing settings for a given gallery required either manual
edits to change and restore the configs file, or automated but perilous
edits in build scripts.
Some of this site's gallery build scripts, for example, resorted to
sed editor scripts on Unix to change a handful of settings
in temporary file copies (here's an intentionally retained
example). With arguments,
settings that deviate from the norm are much easier and safer to tailor per gallery.
For instance, a gallery which wishes to opt in to 2.1 dynamic index
layout
and 2.2 tooltips,
change the index page's colors to a darker
theme, and mod
the order of 2.2 left/right touch swipes can now
be built with the following sort of command-line arguments instead of config-file edits,
from either the console or build scripts (some Windows
tools
use ^ for line continuation instead of Unix's \):
# single line
...thumbspage$ python3 thumbspage.py gallery/ useDynamicIndexLayout=True useToolTips=True thumbsBgColor=\"black\" thumbsFgColor=\"#ffffff\" thumbsBorderColor=\'white\' lrSwipesPerButtons=True
# multiline
...thumbspage$ python3 thumbspage.py gallery/ \
useDynamicIndexLayout=True \
useToolTips=True \
thumbsBgColor=\"black\" \
thumbsFgColor=\"#ffffff\" \
thumbsBorderColor=\'white\' \
lrSwipesPerButtons=True
If the images-folder path (e.g., gallery/ here) is omitted,
it will be requested in the console as
usual.
Any config-file setting can be overridden this way, and any setting not
listed in the command still uses its value in the file
as before. This is especially handy to enable dynamic layout, which is still not
a default in 2.2 despite its growing
usage
(but there's always the next release).
Subtleties: command-line configs are applied after settings in the
configs file have been imported. This works well, but may lead to unexpected behavior
if the configs file assigns one setting name to another: if A=B
appears in the file and B=other appears as an argument,
A will still reflect B's prior file value, not its
other argument value. If this arises, simply reset
A as an argument to the same value as argument B.
Also note that setting values cannot name other settings when coded as arguments;
don't use variables in argument values.
Nit: with command-line configs, there are now three ways
to tailor generated galleries—configs files, configs arguments,
and console inputs. This is largely an artifact of this program's evolution.
Arguments are simply selective overrides for the file's complete settings.
Console inputs are disjoint from the others, and seem redundant with the new
arguments; while they could be made
file/argument settings to simplify this story, this would be a
backward-incompatible change whose costs are well over budget.
For a complete demo of config arguments in action, see the Bash script
here.
Update: thumbspage 2.3 added new config settings
that can be used to override console inputs. When provided, in the config
file or command-line arguments, input values are taken from the settings, and no input
is requested at the console's stdin. This leverages 2.2's config arguments, and
resolves the inconsistency nit noted above.
For more details, see the 2.3 release note and its command
examples.
Optional Tooltips
Version 2.2 adds optional tooltip popups. When enabled, the tips appear
on mouse hovers over index-page thumbnails, as well as viewer-page image,
filename, and Auto and Full buttons. These use simple title
attributes with short text (e.g., "View image" and "View info"), and do
not appear on tested mobiles even with a stylus or mouse.
Because they might also be distracting, and pointless (if not annoying) after initial
usage, tooltips can be turned on or off at build time via a new useToolTips
setting in the configs file and its equivalent
arguments (e.g., useToolTips=True).
The default preset is False for off (disabled), but this is nearly
too grey to call.
Musing: HTML5 localStorage might be used to display tooltips only
on first visits to a gallery, by checking a visits counter, and setting
titles in JavaScript instead of HTML. This was rejected because
browser storage can be disabled or deleted by users,
may record differing values across browsers and devices,
and has quirks in some browsers that qualify as
nonstarters.
For a first-hand look at tooltips, see both the screenshots and live tips in
the gallery here.
Update: thumbspage 2.3 makes tooltips enabled
by default, because the viewer-page toolbar is busier with the new Note;
adds tooltips to the Prev, Next, and Index buttons for consistency; and drops
HTML hrefs on all viewer toolbar buttons to avoid URL popups, which were redundant
and obtrusive. See the 2.3 release note for more info.
Etcetera: OK, UI, Local Views
In addition to swipe gestures, config arguments, and tooltips, 2.2 adds
the following enhancements:
- Larger info "OK"
-
2.2 makes the image-info popup's "OK" button larger
primarily on iOS, where its former default was almost too small to tap.
This button is slightly larger elsewhere too, but bigger is generally
better in UIs.
- HTML alt attributes
-
2.2 adds
alt attributes to HTML image tags for completeness and
accessibility, though they are moot in many use cases. In both generated index and
viewer pages, these appear alongside title attributes added for
the tooltips described earlier.
- Thumbs-table background color
-
2.2 darkens the default background color of index-page thumbs tables,
for better contrast. This color changes from its former
#f5f5f5 to the new
lightgrey.
Though potentially impactful, this is just a preset default, and a partial
concession to the trend towards dark modes. You can change it arbitrarily
with file or argument configs,
and a true OLED dark mode matching viewer pages' preset
black can be easily
had—see the example above.
Be sure to change any auto-edits to look for the config
file's new color, or use config arguments instead.
- Thumbs-table border
-
2.2 changes the index page's thumbs-table border to both use rounded corners,
and appear on all four sides (not just top and bottom), for all
index-page layouts. This makes it easier to tell where the table starts and stops,
especially when it uses fixed layout and scrolls horizontally. See any
example.
Don't forget to use a body margin in custom headers if you wish to offset
the thumbs table; this is automatic in default headers, per the next note.
- Index-page margins
-
2.2 adds a 12-pixel left and right body (i.e., whole-page) margin to
index pages which use a default header. This isn't necessary in viewer pages
(which use an explicit layout), and is moot when using custom index headers
(which take charge of all such matters). In default index pages,
though, this helps set off the prior note's new borders, and accommodates curved
screens better than browser defaults. Cosmetic but
true.
- Thumbnail spacing (dynamic)
-
2.2 changes the built-in horizontal spacing around thumbnail images in dynamic
index-page layout, from 32 to 16 pixels
(which mean 16 and 8 pixels on both sides, respectively). This spacing is used
only if thumbnails are wider than filename labels (else the labels dominate),
and is augmented by the horizontal-padding config
dynamicLayoutPaddingH (which is applied regardless of label
and thumb widths).
The new spacing allows thumbnails with narrow labels to be squeezed closer together.
- Thumbnail padding (fixed)
-
2.2 changes the built-in cell padding in fixed-layout index-page thumbs
tables from 3 pixels to
4 (which is doubled for adjacent cells), to further reduce horizontal
run-together for wide filename labels and increase whitespace slightly. The
difference is so modest that you'd have to be an obsessed web developer to notice.
- Taglines now optional
-
2.2 makes taglines optional in default-footer index pages,
via new config switch
defaultFooterTagline.
These messages—"Gallery built by thumbspage.py" and
"Page built by thumbspage.py" since 2.1—appear
by default at the end of such pages with a link to this program's website, like
this and
this.
As before, they can also be omitted with a
custom but simple FOOTER.html file,
but the new config makes it easy to avoid the mild plugs when the default
footer is used.
Examples start
here.
- Android local views
-
2.2 inserts a usage update in this doc regarding local-gallery views on
Android. This platform evolves
almost too fast to document.
2.1: Thumbs, Layout
thumbspage 2.1, published in final form on August 18, 2021, includes two major
new features, plus a handful of smaller upgrades. This section provides overviews
of its improved thumbnail images, dynamic thumbnail
layout, and other changes.
As usual, you can also view the graphical effects of these changes
in the new 2.1 galleries
here and
here, and
explore their code in the files referenced ahead.
Also as usual, 2.1
download
packages newer than the publishing date above have only minor documentation
and other edits which do not alter program behavior; the most recent of these
non-functional updates was on August 31, 2021.
Improved and Customizable Thumbnails
2.1's first major new feature is the addition of thumbnail-nail enhancements,
both automatic and customizable. Eight new settings in the
user-configurations
file support arbitrary changes to thumbnail
color, contrast, sharpness, and brightness, as well as the quality level
used for saves.
Among these, two precoded enhancements are preset to be on by default:
- Image save quality: noise
-
The first preset automatically boosts save quality to avoid image
noise that
stems from the underlying image library's default JPEG compression. This former
thumbnail noise was most noticeable at higher browser zooms, and in specific kinds
of images.
- Image sharpening: blur
-
The second preset automatically sharpens all thumbs to reduce image
blur
inherent in the underlying image library's resampling filter used for resizes.
Despite using the best filter, this former thumbnail blurring was noticeable
at all zooms, and in all gallery images.
The net effect of these two makes thumbnail images noticeably clearer
on thumbspage index pages in all
galleries and
displays tested,
at a trivial cost in extra storage requirements measured in just kilobytes per gallery
in all test cases. Moreover, the improvement is visually identical for galleries
generated on macOS, Windows,
Linux, and Android, using Pillow 7 and 8.
In addition, another precoded thumbnail enhancement provides a
black-and-white
thumbnails mode, and users may tweak other enhancements' settings arbitrarily
for more custom
effects
(e.g., to lighten thumbnails for darker photos).
For a brief look at the thumbnail presets and other settings in action,
see the new 2.1 upgrades
gallery.
For more details on this release's thumbnail changes, see both the
new configuration settings and their ample docs in
user_configs.py,
as well as the implementation-level details in
viewer_thumbs.py.
In the unlikely event that the two new preset enhancements won't work for your
use case, you can revert to 2.0 thumbnail appearance by simply changing
these presets' settings to False in the first of these files.
Dynamic Index-Page Layout (Experimental)
2.1's second major new feature is the addition of a new layout mode for
thumbnail index pages, which arranges thumbnail columns dynamically to
match page size, and rearranges them on page resizes.
With this new mode, galleries can now choose from two layouts for
index pages:
- Original fixed layout
-
Prior to 2.1, thumbnail-image links were always arranged on index pages using
a table with a fixed number of columns chosen at gallery build time.
In this mode, the table grows and shrinks horizontally with the page, and
scrolls horizontally where needed on smaller displays, but always renders
the same number of columns in all contexts.
- New dynamic layout
-
Beginning in 2.1, thumbnail-image links can instead be arranged on index
pages using a more dynamic model. Rather than scrolling horizontally,
this new scheme chooses the number of columns to show based upon the
page's size, and may show more or fewer columns as the page expands and
shrinks, or the device's orientation changes.
Of these, the new dynamic layout works well on desktop displays, where
pages can be freely expanded and shrunk to display more or fewer
columns,
and responds similarly to page resizes on popup and split-screen app windows on mobiles.
This can make better use of available space, and avoid some horizontal scrolls.
Because it adjusts to match display size, this layout is also arguably more
in line with the responsive web-design paradigm.
On more rigid mobile displays, however, the new mode seems subpar: in some
contexts, galleries may be displayed with just a
single column
in portrait mode (and perhaps more in
landscape),
which eliminates minor horizontal
scrolling, but can make for radically more vertical scrolling in non-trivial galleries.
The impact of this may vary per phone and
content, but is typically glaring.
For example, compared to a fixed four-column table, the dynamic model can
require four times more vertical scrolling on mobiles that display a single
column. For a 100-image gallery, this may mean 20 pages to scroll through
instead of 5, depending on display size. Even assuming one extra horizontal
scroll per page of interest, the fixed scheme still saves substantial
navigation work—and the user frustration it tends to invoke.
Moreover, the new dynamic model:
-
Never expands space between links like the
fixed scheme; extra space is filled with new columns when possible,
not distributed across the table
-
Differs in layout depending on the relative widths of filename labels
and thumbnail images; to simulate columns, cell width must be chosen
from the maximum of one or the other, and the wider dominates
-
Breaks any intended visual associations; side-by-side comparisons based
on fixed rows are impossible, because thumbs are arranged arbitrarily
per display size
-
Has been tested
but not used as extensively as the fixed-columns scheme; given the
vagaries of the web, glitches are not wholly unexpected for some galleries
Because of these tradeoffs, the new dynamic layout is provided as an
experimental alternative to the former fixed-column
tables,
which are still available, recommended, and default in 2.1.
To enable the new dynamic layout for your gallery instead, simply
change the new useDynamicIndexLayout setting in
user_configs.py to True
prior to gallery generation.
If you do enable dynamic layout, you can also customize the amount of
padding between thumbs by setting either or both of
dynamicLayoutPaddingH and dynamicLayoutPaddingV
to any CSS size in the configs
file.
The former is used for horizontal padding (both left and right) and is applied
whether thumbs or labels are wider; the latter is used for
vertical padding (both top and bottom). These settings provide more control over
layout, by expanding or collapsing
space.
Horizontal padding is also a fallback option if program choices prove unwanted,
though layout also depends on the widths of labels and thumbs.
To evaluate the new dynamic-layout mode for your use cases, you can catch
it live at its new example gallery,
or browse all its
screenshots.
To dig deeper into the implementation, you can view the source code of a demo
page,
or study the index-page code generation portions of
thumbspage.py.
Related change: see also this doc's section on console
inputs; dynamic layout removes the
thumbs-per-row prompt, because this is automatically determined from
page size when this layout is selected.
Update:
dynamic layout is still not the default in version 2.2, but that version makes
it easier to enable this option per gallery with config arguments in gallery-build
command lines. Simply use a command like the following at a console or in a build script,
and skip config-file edits:
python3 thumbspage.py imagefolder useDynamicIndexLayout=True
For more details, see the complete coverage of the new config arguments
in 2.2 release notes.
Update:
dynamic layout's horizontal spacing scheme was improved in version 2.3.
It now packs columns tighter, which allows some galleries to render as more
than one column on some mobiles. See the 2.3 release
note for details; this mostly removes
the "experimental" stigma of dynamic layout, though it's still not the default
because it can leave some space empty on the right for some window sizes.
Update: version 3.0 made dynamic
layout the preset default, because it proved itself over time and is
best across a range of device types. Especially for mobile, dynamic
layout's responsive behavior is generally superior.
And So On: UI, Builds, and More
Beyond its new thumbnail-image and index-layout features, 2.1
also includes an assortment of smaller upgrades:
- Log messages
-
2.1 displays the image-folder name in its final console
message for clarity.
A small change to be sure, but this is otherwise nonobvious after many
message lines, especially in logs of larger builds (e.g., see the
automated build scripts ahead). The folder name
displayed is just what was passed in; a full absolute path may be very long,
and
. and .. are not expanded because doing
so can be ambiguous (e.g., a subfolder of . may
have the same name as .).
- Subfolder links
-
2.1 allows the space between automatic subfolder links
to be customized. This employs a new setting
subfolderSpacer in the user-configs file.
This string setting can be changed to any CSS length-unit
value.
Its preset default was also increased from 6 CSS pixels to 7, for easier taps
on mobiles. To see the change live, view its 2.0-versus-2.1
screenshot,
or run the live
demo—ideally
on a mobile device, to gauge its improved tapping support.
- Default taglines
-
2.1 shortens its default-footer
tagline
on index pages to "Gallery built by thumbspage.py" so it's less distracting.
It also specializes this to "Page" for imageless folders, per the
next note. Either way, these can always be removed altogether with a custom
footer.
- Empty folders
-
2.1 avoids generating an empty thumbnails table for folders with no images,
but does generate a page with title, subfolders list, and Top button,
plus any custom header and footer. This can be used to create a top-level
index page with only a subfolder list (and a Top, in case the list is long).
For such imageless folders, 2.1 also drops the
"Image"
in default-header
titles
(it has no images); specializes the default-footer
tagline
to "Page built by thumbspage.py"
(it's not a
gallery);
avoids issuing spurious console queries
(no thumbs or viewer pages are created); and tailors the final build output
line to say
"imageless folder."
For a typical use case for imageless-folder pages, see the 2.1 dynamic-layout
gallery.
- Auto and Full indicators
-
2.1 changes the text of the Auto button on viewer pages to use underline
font whenever a slideshow is in progress. Without this visual indicator,
it may be difficult to know the show's state, especially on initial startup,
and when returning from another page. Here's how the Auto button
now morphs when a slideshow is turned
on and
off with Auto.
The active font deliberately looks like a link, as a reminder that it can be pressed
to stop the show. Italics was also tried but abandoned, because it makes other
buttons shift slightly but noticeably on Android; subtle but true.
For parity, the Full button similarly changes its font as an indicator that
it has invoked fullscreen
mode
and can be tapped to cancel it, but this isn't as useful as it is for Auto,
because Full lasts for just one page, and its activation is obvious.
Unlike Auto, Full font changes are also not supported by all browsers, and
Full will not change for, and will not be able to cancel, a fullscreen mode invoked
by a user outside the page (user-initiated and JavaScript-initiated fullscreens differ).
Full does, however, change for cancellations invoked apart from the button (e.g., Escape keys).
- Embedded thumbs and rotations
-
2.1 deletes embedded thumbnail images, if present, in images automatically
rotated to
be right side up for proper display as in-page elements. Though irrelevant to
thumbspage itself, and rare in general, the new deletion avoids potential
issues in other tools that use embedded thumbs which are arguably miscoded.
Formerly, some such embedded
thumbs could confuse some tools (e.g., file explorers) into displaying tilted thumbs,
because they were not rotated along with the main image. Deleting these thumbs is a
simple yet reasonable fix, given that rotated images are meant for use in thumbspage galleries;
thumbspage makes and stores its own thumbnails separately; other tools will do the right thing
without an embedded thumb, because they already must for other images; and the original image
along with its embedded thumb is always saved to a backup copy prior to rotation.
For both more details and a demo of the new removal at work, see the new
console log.
As detailed there, this issue's scope was initially isolated to a single context
(photos taken on a Galaxy Note 9 and viewed as thumbnails in macOS Finder post rotation),
but was later seen to impact some other file explorers and images shot on other cameras,
and may be aggravated by tools that add thumbnails covertly. The reorientation
demo was also rebuilt with the 2.1 fix;
see its image's
before and
after scene in macOS Finder.
As a fallback: if you're convinced that this isn't a
concern for your images and tools and wish to minimize image changes, you can disable
2.1's thumbnail deletions on rotations by changing the new setting
deleteEmbeddedThumbs in the configs
file. Either way, you can always restore the
original versions of rotated images—including their embedded thumbnails—from
their .original
backups.
Subjective comment: embedded thumbnails in image files seem a very bad idea;
they aren't recorded by all devices, double the workload of both developers and
image-processing programs, and are prone to grow badly and arbitrarily out of sync
with main images over time. Some tools still naively use them anyhow, thereby
propagating a design flaw that makes redundancy a built-in. See also
getUpdatedExifData in the code.
- GUI-mode repair
-
2.1 resolves a minor issue with the thumbnail-folder name used by the
GUI-mode viewer in viewer_thumbs.py—which
you probably shouldn't use anyhow. This viewer is legacy code from its file's
past, and isn't very good as photo viewers go. But as it was, the viewer created
thumbnails in a subfolder that was apt to appear in thumbspage HTML galleries.
To avoid the rare potential, this file now uses the same subfolder name as
HTML galleries; more details here.
- Download packages
-
2.1 upgrades its distribution packages in two ways. First, it now always
ships thumbspage as a zipped folder, instead of individual files.
Unless your unzip tool always creates a folder anyhow, this avoids having to
manage multiple files after an unzip.
Second, because its examples folder passed the
200M mark, 2.1 now provides multiple zips for users to choose from.
From smallest to largest, here are the new
downloads
and their sizes per macOS Finder (last updated for the 2.2 release; see
the main web page
for current sizes):
- Program + User guide — the complete program and guide: 704K
- Examples — the examples folder only, standalone package: 277M
- Program + User guide + Examples — the full package as before: 278M
The first of these contains the complete runnable program, but omits
the examples folder for space, and includes a user guide whose example
links have all been changed to open online content, courtesy of
this script.
- Automated builds
-
And finally, 2.1 fully automates its release, thanks to
shell and Python scripts like
this,
this and
this, plus per-example build
scripts like
this and
this.
This isn't quite as user facing as other 2.1 changes, but serves as an example
of automating gallery builds.
See the new build folder for full details.
That folder also includes a demo of automatically regenerating
and uploading thumbspage online clients when thumbspage's results
change; see that
file for build tips if you manage a website with galleries,
as well as the
generate and
publish scripts
of the online training-gallery
demo
for more inspiration.
Beyond all this, 2.1 polished its code and docs as usual.
Explore the package at large for more details.
RFC:
if you have a suggestion regarding thumbspage 2.1's defaults for
thumbnail quality, thumbnails layout, or subfolder links, please send
feedback by email.
The defaults suffice for thumbspage's host site, but your mileage may naturally
vary. As a rule, thumbspage strives to avoid making choices for you, and all
2.1 defaults are configurable per gallery and builder by design; as always,
tweak as desired.
2.0: Auto, Top, Full
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).
Version 2.0's enhancements were rolled out over four 2020 releases,
from June 26 to October 5.
The rest of this section drills down on each of 2.0's changes listed in the
table below, from earliest to final.
Version 2.0 Changes in Detail
In complete terms, 2.0 adds the following upgrades, with major items
colored black for emphasis:
- Automatic slideshows for viewer pages
-
Version 2.0 adds an
Auto button
on viewer pages, which toggles an
automatic slideshow on and off. When active, the slideshow continually
advances to the next image-viewer page after a fixed pause, like automatic Next taps.
This allows gallery users to step through images without manual navigation.
For an example of the new Auto button, see the screenshot
here;
to test-drive the new slideshow, press Auto in any viewer page of a live
demo.
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. This delay setting is preset to
5 seconds, which seems a reasonable balance of user goals (in 2.1, it briefly
changed from 5 seconds to 4, but was later reset to 5 for usability, as explained
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 by tapping Auto.
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 button 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.
A slideshow's scope spans all viewer pages visited in a browser tab,
because it is based upon the tab's session-state storage.
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.
Update:
version 2.1 later enhanced slideshows by changing the Auto button to underlined
font as an
indicator
whenever a slideshow is on. This was a minor but crucial missing feature of the
implementation, because the toggle's state was otherwise unclear, especially
when first starting a slideshow, or returning to it from another page.
See 2.1's release note for more info.
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.
- Optional floating Top button for large index pages
-
Version 2.0 adds an optional but default floating
Top button
in the bottom right of index pages, which jumps immediately to the top of the
page. It's available and automatically created in all index pages, whether they
use default or custom headers. Viewer pages don't
require a Top, because they are full-window displays with no vertical scrolls.
To sample the new Top button, see the screenshots starting
here,
or scroll down on the live demo page
here.
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:
- Disable Top altogether, with
floatingTopEnabled
- Change the scroll position at which Top first appears, with
floatingTopAppearAt
- Change Top's offset from page bottom to allow for lower content like
toolbars, with
floatingTopSpaceBelow
- Change Top's foreground and background colors, with
floatingTopFgColor and floatingTopBgColor
- Arbitrarily customize Top (e.g., its colors and fonts), by editing
file
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).
- Optional one-page fullscreen display for viewer pages
-
By default, version 2.0 adds a
Full button
to viewer toolbars, which toggles a JavaScript-initiated fullscreen display mode.
This mode comes with substantial tradeoffs: it's limited to a single page,
and does nothing on some browsers—including iOS Safari and iOS Chrome.
Moreover, while this mode can be useful as a quick blowup/zoom feature, some observers
may find it a gimmick that wastes UI space (toolbars now scroll
per the next note, but Full may be initially off-screen for large fonts on small devices).
Hence, the Full button can be omitted on a per-gallery basis, via build
setting
showFullscreenButton in file
user_configs.py.
Where supported, Full does expand and collapse the viewer page
on taps, and its fullscreen may be collapsed by other means on some
platforms (e.g., an Escape keypress). 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 an enlarged image (plus an annoying startup message in some browsers).
Crucially, though, the blowup lasts just for one image page:
any navigation to a new page, 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 handler.
It's usable only in response to user gestures, not page loads
(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. (In short,
a persistent fullscreen would require changing a single page in place,
rather than opening new pages; depending on the design, it might also
sacrifice per-image viewer-page URLs.)
Given Full's limitations, most 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. That said, Full may still be useful as a
quick zoom, especially on browsers without a manual fullscreen option.
Enable or disable as you like; it's your gallery.
Update:
version 2.1 later enhanced the Full button to, like Auto, change its text to
underlined font whenever fullscreen has been
activated by the
page's Full. This is for parity with Auto, but isn't quite
as useful, because Full lasts just one page, and it's obvious that the mode is active.
See 2.1's release note for more details.
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.
In fact, JavaScript is nearly required for thumbspage galleries today,
and this might someday warrant a redesign that changes a single persistent page's
content to support a true fullscreen mode (at the possible expense of per-image
viewer-page URLs). This remains TBD, but users today can manually invoke a
cross-page fullscreen on many browsers and platforms.
- Improved horizontal-overflow styling for border cases
-
Version 2.0 adds and modifies CSS styles to better address viewport
horizontal overflow, by:
- Wrapping the folder name at the top of default index pages,
with a CSS
overflow-wrap: break-word on the <h1> title;
the result for pathologically long cases looks like
this
- Scrolling the buttons toolbar at the bottom of viewer pages,
with a CSS
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. - The iOS Chrome history-stacking bug is no more
-
iOS Chrome had a bug which made it impossible to prevent viewer pages from
being added to browser history during gallery navigation. Its initial
description, and its thumbspage work-around to always stack history on this
browser, were part of 1.6's release notes.
By its version 75, Chrome's history behavior was still broken, but had changed:
pages were erroneously stacked as visited, but browser Backs retraced
navigation paths instead of displaying the same page repeatedly as before.
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).
- Replacement-target comment in the head
-
From the simple-but-useful department—Version 2.0
now generates the following comment in the
<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. Automatic search-and-replace may
also require programming skills, but can be a simple customization
option for sites published with scripts.
Update:
in version 2.1, this feature was finally leveraged by
this script in the new
build folder, to automatically insert
analytics code prior to uploads. In hindsight, this may have been a bit
late to the party; another manual replacement key also snuck into gallery
indexes' custom headers over time.
- Show user-friendly messages on console-input errors
-
A minor usability upgrade: at build time, version 2.0 now displays
user-friendly error messages for invalid parameter inputs at the console,
instead of showing raw Python exception tracebacks as before.
This includes catching break- and EOF-key inputs to cancel a run.
The former tracebacks sometimes included assertion text, but
might have seemed a bit rough to non-developers.
For a demo of the new error trapping and reporting, see this new
console log.
For the input-handling code, see the generator script.
- Info-popup: use a custom dialog instead of alert()
-
Version 2.0 replaces the info popup's former JavaScript
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:
- Scrolls text horizontally when
needed,
instead of wrapping
- Inherits viewer-page background and foreground color settings
- Respects user font-size settings for displayed message text
- Supports copy/paste of displayed message text
- Does not terminate the Full button's fullscreen displays
- And is not treated as a second-class citizen by browser vendors
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.
Update:
as of version 2.3's May 2022 release, the background, foreground (text), and
border colors of the info popup can now be configured to differ from the viewer page
at large, and the opacity of the popup's overlay can be customized too.
See the overviews
here and
here;
the new color and opacity settings also apply to 2.3's Note popup, which borrows
both code and design from info.
- Info-popup: add Device (or Software) line if present
-
Version 2.0 adds another line to the info popup to identify the image's maker if present:
either Device, which identifies the camera, scanner, or other hardware component
that created the image (e.g., "DSC-T30"); or else Software, which gives the creating
program (e.g., "Adobe Photoshop"). Device lines may also have brand appended if useful
(e.g., "DSC-T30 (SONY)").
Depending on image content, the new info line may be present or not, and is formatted
conditionally as follows:
- Device is tried first, and is taken from the
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.
- If device is present, the brand name in the
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.
- If device is not present, the
Software tag's value is reported instead
when available; it's recorded in some drawn images, but this varies by program.
- If neither device nor software is recorded, no extra line is added; this would be
pointless UI clutter.
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 ?
(until 2.3's more general escapes).
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 primarily of 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.
- Info-popup: Taken=>Created if origin date unknown
-
2.0 also changes the label in the info popup's date-of-origin line from
Taken to Created when the date is unknown.
The former didn't quite work for drawn images which are neither
taken snapshot nor taken screenshot,
and the latter still works for PNGs and dateless screenshots in general.
The date-of-origin line is always present, and may now be
labeled Taken (for photos, the original); Digitized (for scans,
since 1.7); or Created (for media of unknown origin date, in 2.0),
according to the data recorded by image-creation tools.
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.
- Info-popup: display dimensions as "Nw x Mh"
-
For clarity, 2.0 also changes the info popup's image- and
display-dimensions formatting, to label width and height with suffix
letters
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.
- Safari work-around: use HTML5 session storage for Auto
-
The new Auto slideshow in 2.0 originally used the
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 macOS 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!
- Safari punt: Auto may stop (or crash) on Back to gallery
-
But wait, the desktop Safari Auto story gets more exciting still. Using HTML5 session
storage as described in the preceding note fixes Auto slideshows to work on Safari in
most contexts: as on all other browsers, they start and stop normally on Auto taps,
and continue as expected on Prev/Next, as well as an index-page thumbnail tap after
Index or browser Back. Even with the fix, however, slideshows on Safari,
only, in some contexts stop after returning to a viewer page with a browser
Back; on rare and seemingly random cases, this may even crash the
page—and possibly browser—altogether.
In testing so far (using desktop Safari 12 and 13 on macOS 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):
- Run an Auto slideshow in an offline local-file gallery (protocol
file://)
- Leave to any online remote page (protocol
https://)
- Return to the local-file gallery with a browser Back (protocol
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
gallerieson mobile altogether (but watch for later clues in theepilogue).
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, macOS, Linux,
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...).
- Silence a pointless and confusing Pillow DOS warning
-
thumbspage 2.0 was patched and rereleased in September 2020 to silence
a bogus
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.
- Image auto-rotations are not optional in thumbspage
-
Short story:
version 2.0 explored the idea of dropping rotations (a.k.a. reorientations)
of tilted images in thumbspage, by propagating sources' Exif orientation
tags to their thumbnails and relying on browsers to rotate. This was rejected
because browsers still don't support rotations universally in fall 2020.
Browsers widely straighten images displayed alone,
but not images displayed as in-page elements—like those in thumbspage
index and
viewer pages.
To be inclusive of all users, thumbspage must rotate images itself,
because some browsers still
don't,
and even some that do today did not
rotate in versions released just a year or two
ago.
This note discusses the alternative
proposal and its later
rejection, as developer-oriented
information only; no changes were made, but the story is typical of
browser-dependent UI development.
The Proposal
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:
- Rotate the source in memory to be right-side up
- Back up the source's file to a
.original copy
- Save the rotated source to its file, with updated orientation,
width, and height Exif tags
- Make and save a thumbnail from the rotated source
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:
- Make a thumbnail from the source in memory
- Copy the source's Exif orientation tag to the thumbnail
- Save the thumbnail to a file
- And hope that every browser on the planet does the right thing for in-page
display of unstraightened images, both source and thumb
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.
The Rejection
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).
1.7: Info, Exifs, Etc.
Version 1.7 was initially released in February 2020, and repackaged as
point releases until June of the same year.
Here are the highlights of the 1.7 changes introduced by each release package:
-
The February 17 initial release of 1.7
responds to filename taps, with a new image-info popup;
responds to image taps, with a full-size and browser-native display
(just like the Raw button);
handles large-font user settings without clipping images or running toolbar buttons together;
uses more of the page for larger image displays;
and drops hover effects which are unevenly supported on mobile browsers.
-
The March 6 point release of 1.7
shortens labels and adds a display-size line in image-info popups;
upgrades thumbnail quality for shrunken images and some GIFs;
retains and updates Exif tags for rotated JPEG images;
supports a new command-line argument for folder name;
and discusses image size in online galleries,
and links to a new program that can shrink images for you.
-
The May 14 point release of 1.7 labels dates of photo scans as "Digitized"
in image-info popups (instead of "Taken");
uses JavaScript image scaling for landscape views on iOS in all
browsers (instead of CSS-based scrolled displays);
extends disabling of iOS Safari landscape upscaling to index
pages (since it's now all-in on this use case);
and applies a few minor documentation tweaks.
-
The June 13 final release of 1.7 works around an exception in the
third-party piexif library,
which cropped up for an uncommon Exif tag present
in a photo shot on one specific smartphone's camera. A miscoded tag perhaps (and
generated pages are unchanged), but before the June patch,
such piexif exceptions could terminated thumbspage runs;
despite the meme, "batteries included" development is a mixed bag.
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.
Version 1.7 Changes in Detail
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 1.7 changes, from first to last, with
major items colored black for emphasis (and
time-strapped readers):
- Avoid image clipping at large font settings
-
When calculating space available for the image, thumbspage viewer pages
formerly used a constant to account for used space. Though rare, this
could sometimes lead to clipping of the image's bottom
border
and even part of the image's lower
content,
if the user applied a larger-than-usual
font setting. This occurred only for very large images (e.g., mobile
portrait screenshots) and wasn't possible in all browsers, but could happen
on both mobile and
desktop
devices.
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.
- Avoid button run-together at very large font settings
-
On smaller displays and devices, thumbspage viewer pages formerly scaled
up the font size of toolbar buttons for accessibility (to
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.
- Open a new info popup dialog on filename click/tap
-
When JavaScript is enabled, Version 1.7 viewer pages now catch clicks and
taps of the filename at the top of the page, and open a simple image-info
popup dialog in response. Here's what it looks like on
desktop and
mobile
browsers.
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:
Later still, versions
2.2 and 2.3
further enhanced info popups with touch swipes, customizable
colors and opacity, and more; see later releases for more info.
- Open the full image on image click/tap, just like Raw
-
When JavaScript is enabled, version 1.7 viewer pages now make image clicks and
taps the same as activating the toolbar's Raw button for convenience, opening
the image directly in the underlying
browser.
This is generally a quick way to view the image at full size and zoom and pan,
and is especially handy for the smaller image displays in landscape mode on
most mobile devices; here's one
normal and
tapped.
On devices that support hovering, the cursor also changes to a pointer
over the image display to make the new action more obvious.
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.
- Note version number and generation date in all pages
-
Though not user facing, version 1.7 now records the thumbspage version number
and page-generation date/time, in the comments of both the index page and
the image-viewer pages it generates. These notes appear after the image
table's code in the former, and near the top of the latter, and can be located
by searching for
<!-- 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.
- Viewer-pages and clean-thumbs default to yes in console prompts
-
The console-input reply for viewer-pages generation
now to defaults to
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.
- Allow folder name to be passed in as a command-line argument
-
As described earlier, Version 1.7 adds a sole and
optional command line argument: the name of the folder to be processed into a gallery.
This allows you to use shell auto-completion for long folder names (though this may
work better in a console than an IDE).
When provided on the command line, the images-folder
name isn't requested interactively, but other parameters still are; if you wish
to avoid inputs altogether, use the usual < 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.
Update:
versions 2.2 and 2.3 generalized thumbspage
command lines further, with both config-file and console-input overrides coded as
setting=value command-line arguments, though the
1.7 folder argument still works as noted here in its unadorned form.
See later releases for details.
- Skip Unix .* hidden names in subfolders bullet lists
-
A small but useful mod: folders named with a leading
. 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.
- Remove tkinter dependency for building on Android
-
Though introduced between 1.7 and 1.6, 1.7 incorporates a minor February 2019
change in this module, which avoids all
tkinter dependencies except when using the simple and obscure GUI-viewer
mode (essentially, all GUI-mode code is now nested
under a
__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.
- Drop hover italics/underline effects for toolbar buttons
-
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 touch screens (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 touch screens.
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.
- Improve thumbnail quality for shrunken images and GIFs (Mar)
-
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
macOS 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 host website.
Update: version 2.1 more broadly improved thumbnails
by sharpening images and boosting save quality. The net effect removes both
resize blur and compression noise in earlier releases' results.
See 2.1's release note.
- Retain—and update—Exif tags in rotated JPEG images (Mar)
-
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.
Update:
on a related note, version 2.1 and later now delete embedded thumbnail
images, if present, so that their former Exif tags don't confuse some
file explorers. See the update.
- Index pages: avoid text upscaling in iOS Safari landscape (May)
-
As of 1.7, thumbspage now emits a
-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.
- Info popup: label scans as "Digitized" instead of "Taken" (May)
-
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 the prior release's 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.
- iOS landscape: use JavaScript image scaling in all browsers (May)
-
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:
-
Safari landscape views render perfectly,
like this;
JavaScript scaling in this context works without issue, and no longer requires a scroll.
-
Safari portrait views are granted more room for image
display;
this mode was never
broken, but
every pixel helps on small mobile devices.
- Just as importantly, tapping a thumbspage toolbar button at the bottom of its
image pages no longer pointlessly summons the Safari toolbar, requiring a bogus second tap;
this is a major fix for every website with bottom-of-page
widgets—including
thumbspage's
host.
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
down-scroll).
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:
-
On Android, thumbspage image pages work well in either portrait
or landscape orientation modes.
This has not changed since 1.6's JavaScript scaling was introduced.
-
On iOS, thumbspage image pages work well in portrait mode in all
browsers, but landscape support is convoluted by former defects
in Safari, and hence differs by iOS version:
-
Before iOS 13, landscape views render best in any browser
except Safari. Landscape works in Safari too, but requires
scrolls to see image pages in their entirety.
-
As of iOS 13, landscape views render well in any browser,
though Safari users are best served by forcibly hiding its toolbars with
its new "aA" menu setting.
-
On all iOS, a legacy gallery-build option can revert to the former
CSS scaling for Safari landscape, but the default JavaScript scaling works at
least as well.
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...
- piexif work-around: fix rare failures on some rotated images (Jun)
-
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.
1.6: Scaling, Rotation
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.
Dynamic Image Scaling
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:
- On desktop browsers
-
This is a complete solution. Images are now scaled to fill but not
overflow available window space both initially and when windows are
resized, on all desktop browsers and platforms tested—including
Chrome,
Firefox,
Safari, Edge,
and Internet Explorer 9 and 11, across macOS,
Windows, and
Linux.
Users may freely resize their window to change the size
of the image, but need no longer resize to view it in full.
- On mobile browsers
-
This is a major improvement. Images are now scaled to fill but not
overflow available display space both initially and on device orientation
changes, on all mobile browsers and platforms tested—including
Chrome
and Firefox on Android, and Chrome,
Safari,
and Firefox on iOS.
Taller images, for example, may now occupy more available
space.
On mobiles which support popup or split-screen windows, images are also resized
with window-size changes, though this may be most useful on larger devices.
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.
Image Auto-Rotation
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-prerotate-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, especially for in-page elements like those in thumbspage
viewer pages; thumbspage must rotate because some browsers don't.
See the 2.0 note and
demo.
Update:
as of version 2.1, embedded thumbnails in images automatically rotated for
in-page display are automatically deleted, to avoid issues with tools like
file explorers that may grow confused if the main image is rotated but its
embedded thumbnail is not. See the 2.1 release note;
deleting is substantially simpler than updating the nested thumb.
Other 1.6 Changes
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 macOS 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.
1.5: Viewer Pages
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.
Image Viewer Pages
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.
Other 1.5 Changes
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?).
1.4: Mobile
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.
1.3: Unicode
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 macOS ("/")
along the way, though some docs may still retain their former Windows bias.
1.2: Styling
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.
1.1: Subfolders
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 feature can be used to build nested galleries, or
folders with supplemental gallery information.
This feature is on by default. It creates a links
list with one entry for every subfolder nested in the images
folder. The links in turn open the subfolder, with a result that
varies per usage mode. For example, online views may automatically
open the subfolder's index.html page or web-server
index, while offline views open a simple directory listing.
See your web server for online configurations, and click
an index.html manually when offline.
As a convenience, automatic subfolder-link lists skip (do not display)
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). You can use
folder names that start with a _, for instance, for related
content that you do not wish to publish in your gallery.
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's code 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 narrative, 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,
and subfolder index.html files must be clicked manually offline as
noted above.
The tree is not walked automatically because its folders may have arbitrary content,
and subfolder links do not automatically open index.html files
because some subfolders may not have them (e.g., a docs-only
subfolder).
If you really want subfolders' index files to open automatically when
offline, you might consider running a web server on the local
machine.
Update: version 2.1 now allows the space between
subfolder links to be customized with subfolderSpacer in
configs, and increases
this setting's default from 6 CSS pixels to 7 to make taps easier on mobile devices.
See 2.1's release note.
1.0: The Basics
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.
Development Notes
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:
- Info popups display a subset of Exif tags
-
As of 2.0, thumbspage displays up to six lines of mostly static image information
in its info popups
on filename taps.
As noted earlier, there's much more that could be displayed
(Exif defines very many
data items,
including GPS location info), but this
must be weighed against UI simplicity. Though highly partial, the current subset of data
displayed seems likely to align with the primary interests of both users and creators.
- Embedded thumbnails are ignored
-
Per the Exif standard,
some images may embed prebuilt thumbnail images. thumbspage by design instead
uses custom-built thumbnails stored in a subfolder, because embedded thumbnails
are not guaranteed to be present; may not reflect changes made to the
full image; and cannot easily be pulled out dynamically by JavaScript code
running only in a browser, under thumbspage's serverless model.
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. That said, it's not impossible that some file explorers might
become confused, and display a still-flipped embedded thumbnail; improvements TBD.
Update:
as of version 2.1, embedded thumbnails in images automatically rotated for
in-page display are automatically deleted, to avoid issues with tools like
file explorers that may grow confused if the main image is rotated but its
embedded thumbnail is not. See the 2.1 release note.
- Image types are not universally supported
-
As described earlier, some web browsers do not support
TIFF images directly—most notably, current Chrome and Firefox.
TIFFs may work in some such browsers with installed plugins, and may be displayable
via complex JavaScript/canvas server-based techniques that are well beyond the scope of
this project. Rarer image types are even more unlikely to be supportable.
Safari, for example, is scheduled to support WebP but does not today; and was
seen to initially display—but later crash on—an
index page with TIFFs, TGAs, and PBMs.
Ultimately, this seems an inherent tradeoff for viewing images in browsers:
portability is enhanced, but so is third-party dependence.
Image conversion may be the best work-around. For more pointers, try a
search.
- Rotations drop Exif tags (closed)
-
As described earlier, rotated images lose their original
Exif tags, because this is how the
Pillow library saves them by default. It may be possible to restore some manually,
but it's unclear if this should be done; rotated images are not the same as original
photos (e.g., former location and date tags don't quite apply).
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.
- Android Chrome scrollbar oddment
-
As described earlier, Chrome on Android (only) may
very briefly display a vertical scrollbar when viewer pages are loading, despite this
program's best efforts. This is a minor cosmetic issue and is probably a browser
bug (or normal idiosyncrasy?), but a work-around proved elusive.
- Extra-files overhead
-
As described earlier,
this script adds 2 files for every 1 source image (thumbnail image
and viewer page). That's negligible in most use cases, but may
become significant in archives with very many files (10k images
means 20k extra files). This might be addressed by using a single
thumbs file instead of a folder (see
PyPhoto's pickle-file
scheme),
but that would be complex and slow compared to the direct page-to-page
and image links generated by thumbspage. It may also require cross-page
state, local storage access, or a server, and break cross-platform
or offline use. As is, users can generally zip thumbs folders where needed.
- Parent-folder links
-
A ".." parent up-link is not generated in automatic subfolder
lists; should it be? The parent may or may not have images,
and may be altogether unrelated.
Conversely, it's impossible to generate direct down-links to index
files in subfolders, because subfolders may not be image folders.
Both ideas are probably too automatic to be useful—or even correct—in
all cases.
- Index-file location
-
Creating the index file in the image folder might preclude
use of some page-generation tools (e.g., the
trnpix client's footer must
copy HTML code that would be added automatically if accessible
to site-generation tools used). Most likely, though,
accommodating every site build tool is impractical and impossible.
- Meta tags for custom headers
-
This program might generate a doctype and meta content-type tag in
all index cases—not just for default headers—but that
would limit doctype and Unicode options in
custom headers. As is,
custom-header files get complete control of header content, by design.
- More code clean up
-
Even after refactoring to functions in 1.5,
there are still a lot of globals in this program's code; clean up
more in a future release? Alas, this program's origin as top-level
script code is an enduring legacy.
- Mobile landscape scaling (closed)
-
Which is better for 1.6 images scaling on
mobile devices in landscape orientation: iOS
scrollable
or Android
shrunken?
The former was mandated by iOS's lack of display-size support and mandatory
obscuring toolbars, but may be preferable to some users.
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.
- Chrome history destacking bug (closed)
-
Chrome on iOS has a
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.
- More user customizations
-
As noted earlier,
colors on both page types (and much more) can now be customized in
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.
- Dynamic index-page columns
-
The number of columns on the generated index page might be dynamically
configured in JavaScript from viewport size on resizes (much like the current
dynamic scaling of view-page images), but this may be complex (e.g., label
font size would matter), could be too jarring as windows are resized on desktop
browsers, and may make for very long pages on mobile.
Update: tables are fixed grids that don't support
automatic wrapping on resizes, but it's possible to layout image links
with cells that do wrap to display size dynamically by generating CSS and
HTML code of this sort:
#thumbslinks>div {display: inline-block; vertical-align: top}
<div id="thumbslinks">
<div>...image link and filename label...</div>
<div>...image link and filename label...</div>
</div>
Unfortunately, this doesn't retain fixed-sized columns like tables do;
cells are scattered randomly across the page. There may be ways to
get this to work better (e.g., force all cell sizes to the longest filename
label), but scrolled tables seem just as reasonable, and this remains TBD.
Update: release 2.1 implemented
dynamically sized/resized index-link tables, using the layout model
above, along with fixed cell sizes and CSS styling that tries to simulate
columns with the maximum of thumbnail-image and filename-label widths.
The new dynamic layout is available as an alternative to the former fixed-column
tables, but not enabled by default, because it typically yields just one or
two columns on mobile: this avoids some minor horizontal scrolling, but makes
for much more vertical scrolling in non-trivial galleries. Fixed
tables seem a better scrolling tradeoff for mobile, but your needs
may naturally vary. PC-only galleries, for instance, might prefer the
dynamic model; on desktop, the new model shows more or fewer thumbs per row
on window resizes, but doesn't stretch or shrink the space between them.
See 2.1's release note for details.
- Image information display (closed)
-
In principle, this program's results might also display image metadata
(e.g., date-taken and location tags) if present. On the other hand, this
wouldn't make sense for use cases like program documentation and other screenshots.
More fundamentally, this would clutter and complicate viewer pages (minimally,
with a new "Info" button), and, given thumbspage's intentionally static,
server-optional paradigm, would require build-time generation of JavaScript
popup details or additional pages—all of which might suffice to break
this program's simplicity and usability.
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.
- Browser fullscreen-mode options (closed)
-
Web browsers are starting to settle on a JavaScript API for
fullscreen display,
which might be used for thumbspage index and image-viewer pages. Unfortunately,
this API today is both divergent and supported unevenly across popular browsers
(yes, surprise!); its use must be triggered by a user action (e.g.,
a new Full button); and it seems too awkward and jarring on desktop platforms to
adopt for image views (e.g., an odd exit-key message may be posted temporarily).
Moreover, fullscreen may in thumbspage be no better than Raw/image taps (especially
when paired on desktops with a manual window maximize). Fullscreen may be best used
for long-lived content like videos, not action-based galleries.
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.
- Automatic slideshows (closed)
-
It might be useful to add an automatic slideshow option, which advances through a gallery's
image-viewer pages automatically after timed delays (which are straightforward in JavaScript).
Unfortunately, this would require additional UI elements (e.g., a Show button) which would
crowd and complicate the viewer display which is already cramped on
mobile. This also raises
substantial implementation issues: because thumbspage's statically generated pages run
without a web server, they have limited options for passing state between pages to notify
the next of an active slideshow. To be researched.
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.
- A floating "Top" for index pages (closed)
-
thumbspage index pages with very many thumbnails might benefit from a floating Top button,
of the sort used in this doc. This was rejected, because it would clutter the
display badly; may be awkward on mobile, where index the table is horizontally scrolled;
wouldn't work if JavaScript was disabled; and seems unnecessary for all but the most
pathologically large indexes. As is, pages with 2K images are reasonably usable,
and galleries should as a rule be capped at a few hundred images to avoid user overload.
More fundamentally, there may be nothing at the top of the page worth jumping to:
indexes are just
tables of links.
Navigation aids are great, unless they're also pointless.
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.
- Can reorientation be dropped? (closed)
-
As described in this 2.0 note, it may be possible
to reimplement thumbspage without rotating source or thumbnail images, by copying
sources' orientation tags to their thumbnails. This
remains to be proved and is described in full in the note; it's relisted here
so that it might get some attention in upcoming release cycles.
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 browsers don't.
- Per-image notes?
-
In principle, it might be useful to add a per-image note feature:
for any image file
filename.ext, if a text file
filename.txt is also present in the images folder,
thumbspage could generate an index-page link which opens the text file when
tapped. This would support documentation more specific than the general
opening narrative available in a custom header file. This hasn't been
pursued, because there seems no good way to do this without cluttering
what is an intentionally simple UI: hover events won't apply to mobile,
links on filename labels just below images would be difficult to tap,
and adding new widgets would be too much. In the end, thumbspage
galleries are simple on purpose, and generally prefer to remain that way.
Update:
version 2.3 added per-image notes anyhow, as described in full
here. For the UI, this took the form of a single
Note button on viewer pages (with line-throughs when notes are absent),
along with an equivalent up swipe gesture on touch. Notes themselves
were limited to plain text in .note files in the images folder,
with content popped up in-page to be less jarring than separate files.
This is arguably simple, and provides a new narration component
to galleries that's sorely needed in many use cases; it's impossible to
describe photos in an index-page preamble, once the number of images
passes the trivial.
- How about a GUI?
-
Also in principle, a GUI could use widgets to input thumbspage's target folder and console
prompts; might do the same for options in the
configurations module; and may be able to simply spawn
the current script with the results (e.g., with an inputs file and a custom module).
Packaging such a GUI as a frozen app or executable would also bundle Python and Pillow,
making installs easier, and insulating the program from future changes in either.
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.
More Resources
You've reached the end of this guide, but there's more to the thumbspage story:
- For more thumbspage documentation
- See the archived (and now quite dated) closed issues
and developer notes in
docetc/more-docs-trimmed.
- For thumbspage example sites
- Check out the live-demo and example links in
the Resources section of this program's
web page.
- For related image programs
- Explore the
tagpix
photo-organizer script and
PyPhoto
image-viewer GUI, both available at
learning-python.com.
And may all your monkeys be right-side up.
|