thumbspage — Turn Folders into HTML Image Galleries

Version:  1.7, May 14, 2020 (changes)
License:  Provided freely, but with no warranties of any kind
Author:  © M. Lutz (learning-python.com) 2016-2020
Install:  Unzip thumbspage's download, install the Pillow library
Run with:  Python 3.X, on any platform supporting this and Pillow
See also:  web page, live demos, more examples, source code

This is the thumbspage content-creators guide. It includes an overview of the program, detailed usage information, and a look at what's been upgraded in each release. Whether you consider yourself a developer or an end user, you'll find resources here to help get you started building image galleries with thumbspage.

Because thumbspage adds items to the images source folder and may rotate images there, you are encouraged to read this guide first—especially its usage caution—before running thumbspage on valued photo collections. But if you're in a hurry: for a quick preview, try a live demo, packaged examples, or the code. You can also click the image above for screenshots, and take a brief tour of this program at its web page.



This section introduces the basics of thumbspage's roles and operation. Read this 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, in a format that's both simple and noticeably better than browser defaults.

thumbspage turns an images folder into an easily viewed gallery. It automatically makes image thumbnails, an HTML thumbnail-links index page, and HTML image-viewer pages for all the images in a source folder, all of which can be customized by content creators. The static results can be viewed offline or online in any web browser.

What thumbspage Does

In a bit more detail, given a folder of image files, this program generates an HTML index page with generated thumbnail links for each image in the folder. This page's links in turn open either the full-size images directly using browser-native (i.e., built-in) display; or generated HTML viewer pages with the image scaled to your view, gallery navigation links, and file-info popups on filename clicks.

The net effect is intentionally static: generated results reflect the folder's contents at build time only, but do not require a web server, and can be viewed both offline and online in any desktop or mobile browser. As such, this program can be used both for websites, and non-web use cases such as program documentation and general viewing.

When run (using the techniques explored ahead), this script skips non-image files; uses optional header and footer HTML index inserts; makes an optional bullet list for subfolders in the images folder; and creates the output index page in the images folder itself, along with a subfolder for thumbnail images and viewer pages' HTML.

Using thumbspage Galleries

After running this program, you can view or publish its results in a number 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.

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:

You can read the full story on new releases ahead. Read on to the next section to learn how to use thumbspage to view and display your photos.

User Guide

This section describes thumbspage install requirements, inputs and results, customization options, and other operational details. It's a comprehensive tutorial that doesn't assume you are already a command-line wizard—which means advanced readers may want to skim parts meant for others.

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

thumbspage 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 itself, download its zipfile from the following web page and unzip it on your computer:

thumbspage 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):

thumbspage will work on any platform that runs Python 3.X and Pillow, and has the required folder and file access permissions. For example, this program has been verified to run on Windows, Mac OS, Linux, and Android. Be sure to adhere to the latter's unique permission rules when generating galleries in Python Android apps (there's more on Android installs and usage ahead). thumbspage likely works on the similarly constrained iOS too, but this is untested. For a recent related Pillow install pointer, see 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, and does not require a separate install.

A note for developers: Pillow is used from Python for thumbnail generation, image rotation, and Exif-tag processing, all at gallery-build 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.

Running thumbspage

Once you've installed thumbspage and its required tools per the prior 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 Mac OS and Linux, and Command Prompt on Windows) and after opening it in most Python IDEs (e.g., PyEdit and IDLE).

thumbspage's main options are selected with five console replies, or their enter-key defaults, on each run. The following example session gives user inputs in bold font. In its prompts, inputs are described in [], and defaults are given in () (on Windows, be sure to use py -3 at the start of your command line, and \ instead of / in your pathnames):

/.../content$ python3 /MY-STUFF/Code/thumbspage/thumbspage.py 
Images folder path [. or dir] (enter=.)? trnpix
Clean thumbs folder [y or n] (enter=y)? y
Thumbs per row [int] (enter=4)? 
Thumb max size [x, y] (enter=(100, 100))? 
Use image-viewer pages [y or n] (enter=y)? y
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.

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.

Reply #1 is where you specify the source-image folder, which is also where results will appear. This reply accepts an absolute or relative folder pathname. For example, entering folder means that folder in the directory where the script is being run, and /folder/folder and C:\folder\folder denote absolute paths to your image folder on Unix and Windows, respectively. A solitary . means the directory where the script is being run, and is the default.

You should generally clean the thumbs folder (reply #2) unless images have only been added, and use viewer pages unless they don't work well in your use case (reply #5). There's more on both of these options in usage notes ahead.

Replies #3 and #4 allow you to tailor index-page thumbnails on each run:

Other Usage Modes

Besides the basic console interaction of the prior section, thumbspage also allows you to pass in the folder name as a sole command-line argument instead of a prompt reply. This allows you to use shell auto-completion on long folder names, though it's probably more useful in a console window than an IDE. Folder argument or not, you can also automate a launch by using command-line < syntax to provide canned parameter inputs one per line in a piped-in text file. These options yield at least three ways to start the program:

$ python3 thumbspage.py
Images folder path [. or dir] (enter=.)? trnpix
...4 other parameters prompted from input...

$ python3 thumbspage.py trnpix
...4 other parameters prompted from input...

$ python3 thumbspage.py < inputs.txt

In the last of these modes, parameters in the piped-in file inputs.txt would like look this (use an empty line to accept a prompt's default, and omit the first line if you provide foldername in the command line):



Naturally, you can combine all these with shell syntax for pipes and output redirection (| and > file) which are beyond this guide's scope. For more thumbspage usage examples, browse its examples folder. The console input prompts and arguments above are really just the first level of options in thumbspage. The sections Customization and Usage Notes that follow cover additional configuration options and usage details. First, the next section covers more basics for users new to running programs from command lines.

Update: the optional folder-name command-line argument was added in version 1.7. To better reflect this, 1.7 also moved folder name to be the first prompt when not provided on the command line; this invalidates some older session logs where folder name was asked later, but the difference is trivial.

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. This is probably simpler than it sounds; as noted, the pathname you input at reply #1 can be either:

For instance, when running thumbspage via command lines, you can "cd" 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 usual, 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:

> py -3 thumbspage.py 
Images folder path [. or dir] (enter=.)? C:\MY-STUFF\camerauploads\imagefolderhere

If you'd like more background on command-line use, you'll find both online and brick-and-mortar resources that go into more detail. Here, the next section moves on to show you how to make your galleries more unique.


The most common thumbspage 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 (described ahead).

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 both, one, 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 examples in folder examples/ here. In brief:

Provides all of the page's HTML code up to the generated thumbnails table (or subfolder links, if used). It 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 (tip: generate a default index page for boilerplate header code to copy). This preamble should be followed by the start of the <body> section with any informational <p> paragraphs or other content. For instance, header content can be used to describe the images on the page.

Subtlety: a Unicode type listed in a custom header file'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. Also note that header files may wish to accommodate or disable automatic subfolder links: see version 1.1 notes ahead.

Provides the remainder of the page's HTML following the generated thumbnails table. It should add any post-table content and close both <body> and <html>. For example, footer content might include navigation links or informational text.

The generated thumbnail index table's code is self-contained, and requires no support code in a custom header. Conversely, a HEADER.html file can use CSS style code to alter the thumbs table in ways not supported by basic user settings (e.g., to tailor index-table font). For examples of this technique, see the docstring in user_configs.py, as well as the online demo site here.

Viewer-Page Template File

As of 1.6, viewer pages can also be changed arbitrarily by editing the template file template-viewpage.html in this program's install folder (use "view source" in your browser to see its code). For example, such edits might add site icons or navigation widgets specific to your usage or site. Edit with care (this file's code is fragile!) and rerun thumbspage to use your customized template.

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 images' files...
    Optional HEADER.html
    Optional FOOTER.html
        Thumbnail-image files...
        Viewer-page files...

Open the generated index.html file to view the thumbspage gallery, and package the entire images source folder to distribute. In addition, some files in the thumbspage install (unzipped) folder are available for user customizations:

thumbspage install folder/
    Implementation files...
Edit user_configs.py and template-viewpage.html 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.

Usage Notes

This section collects assorted usage pointers for thumbspage users. Though arguably random, it covers some of the most common issues and border cases that may arise when using the program.

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.

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.

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, 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. Because of these constraints, the support story today reads as follows:

Some browsers may ask to auto-open some exotic image types in another program, but this isn't the same as in-page support. If your TIFFs (or rarer) 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 JPG. 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.

One browser-specific caution: a somewhat dated Safari (2015's version 9) on Mac OS has been seen to crash altogether when trying to display a PBM in the mixed-types demo pages, for unknown reasons that are well beyond the scope of the thumbspage project. Safari updates or image conversions are the suggested remedies.

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.
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 details ahead.
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.
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, which generally includes all JPEG photos created by recent devices.

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 utility script that restores all originals from backups; and 1.6 release notes in Version History ahead for more background.

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.
Beware browser settings
Some browser settings that may appear harmless can adversely impact thumbspage galleries. In particular, the native image display on some Android Chromes may initially render some images oddly and too large in portrait orientation, but if and only if the seemingly unrelated "Force enable zoom" is selected in Accessibility Settings.

The "Raw" links of thumbspage viewer pages trigger this effect, but it is not caused by this program—it also happens when visiting images' URLs directly, completely outside the script's pages. It's also limited in scope—it occurs for some images only, and never when the setting is cleared.

Users may be best served by clearing this setting in this use case. For context, try a search on "android chrome force enable zoom displays images too large" like this, or the possibly related bug report here. At the end of the day, thumbspage is completely dependent on browsers which change frequently and arbitrarily, and this issue is sadly typical of their current fragility. (Indeed, at times it seems a website could be laid low by a good sneeze...)

A word on image size, speed, and politeness
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 perfect world, online images would by now be immune to the scourges of metered access and traffic bottlenecks. In the world we inhabit, profit will probably always trump societal need, and the web's mass popularity will probably always best its technological progress.

Update: per testing so far, shrinking large images with the shrinkpix program mentioned above both removes observed slowdowns, and seems to yield faster views. The primary downsides are minor and rare reductions in full-size image quality (described in shrinkpix's caveats), and potentially degraded quality for thumbnails. thumbspage 1.7 solved the latter issue by converting some images to "RGBA" color mode temporarily when making their thumbnails; this produces results as good as for unshrunk originals, and has the added benefit of improving thumbnail quality for some unshrunk GIF images too. See the 1.7 update note and shrinkpix docs for more details.

Update: with the benefit of hindsight, most of the speed issues related to images at thumbspage's site stemmed from web-host speed, not image-file size. Speed issues were finally resolved in full in Spring 2020, by moving this site from its former GoDaddy host to an AWS Lightsail VPN server. The speed increase for thumbspage galleries was staggering, and far surpassed the gain from reduced image-file size (which was so overshadowed by host sloth, that it could not be reliably measured). Still, shrinking images was important for users on metered-bandwidth clients; images at this site are all now 500K or less (and average about half that size), and that's much less to ask of casual browsers than the former 6M bandwidth bombs.

Update: for completeness, it's worth noting that you can shrink a website's images dynamically with Apache's mod_pagespeed (a.k.a. PageSpeed) module. This ambitious module attempts to optimize size of fetched images by automatically resizing and caching for future requests. Unfortunately, you have little control over this process—or the quality of the images it produces. Worse, this module also mangles page code for transmission (a rude nonstarter for sites that double as page-coding resources), and abruptly crashed this site's Apache server when applied by default by a Bitnami install stack (despite the module's experimental "Incubating" status). Your mileage may vary, but static image-size reduction seems a more deterministic—and safer—approach.

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 example from the book where parts of thumbspage first appeared. It works on Windows (any version) or Unix (Mac OS and Linux) as follows:

c:\...\thumbspage> py -3.3 viewer_thumbs.py examples\unicode\images   # Windows

/.../thumbspage$ python3 viewer_thumbs.py examples/unicode/images     # Unix

This is, however, a very basic and limited viewer GUI (and today even confesses as much). The book's later PyPhoto example is a much more useful GUI, with navigation, image scaling and zooms, and scrollbars for both indexes and images (as seen here and here). More recently, a much-upgraded PyPhoto is now available standalone, at the following site (it's a bundled PyGadget):


Also note that thumbspage itself can serve as an image viewer too. Because its results can be viewed both online and offline in any desktop or mobile browser, they can be used for same-device viewing; your browser becomes the GUI.

Usage Caution

Finally, a word from the legal department. thumbspage has been tested extensively and used successfully on all types of photo collections, and will likely perform well on yours too. It is provided freely because it can help you view and display your photo libraries. Given the many ways that computers can fail, however, a word of caution is in order:

By design, this program will modify your images source folder in-place. It adds an HTML index-page file (by default named index.html) and a subfolder with thumbnails and HTML viewer pages (by default named _thumbspage), and as preconfigured rotates any tilted images after saving backup copies of their originals with .original extensions. Run this program on folder copies if you don't want it to change your valued photo collections directly.

All that being said, keep in mind that you can easily delete the file and folder added; original versions of rotated images can be easily restored with the included script restore-originals.py; and both image rotations and their backups can be disabled in user_configs.py. Moreover, if you always run thumbspage on a copy of your source images folder, your originals will never be changed by the program in any way. Still, the importance of your photos merits a complete understanding of any tool that modifies them—this one included.

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 may find additional context here too (and the border between thumbspage users and developers is porous at best).

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 may also work in some contexts.

For readers who want to save a Top click, here's an index to the version coverage in this section:

1.7: Info, Exifs, iOS

Version 1.7 was initially released on February 17, 2020, and repackaged as point releases on March 6, 2020, and May 14, 2020. Here are the highlights of the 1.7 changes introduced by each release package:

Most 1.7 changes appear on generated image-viewer pages, and all can be found by searching for [1.7] in the system's code files as usual. For examples of all the visual changes in 1.7, see its screenshots page.

Version 1.7 Changes in Detail

The following list provides more detailed reviews of changes introduced across 1.7 releases; the later and longer come with summaries for readers in a hurry:

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.

Open a new info 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; 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.

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.
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.
Change viewer-pages and clean-thumbs defaults 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, 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.
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 after running both this command (sans its "-y") and pip install Pillow; as well as the tkinter-aware Pydroid 3 after running the same pip command in its Terminal or using its Pip. Especially when using a keyboard or tablet, this makes running thumbspage on Android a reasonable goal.

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 touchscreens (e.g., mobile browsers).

For example, hover effects commonly get stuck on after a tap on Android mobile browsers, and aren't cleared until the next user event; this is especially poor if the tap does not switch to a next page—like the new filename tap in 1.7 above. Hover effects may also require a double tap to activate the main action on iOS devices (one tap for the hover, one to activate). In thumbspage, hovering was formerly disabled for smaller screens via CSS media queries, but its problems could still crop up in landscape mode on mobile devices. Hover effects may be superfluous for toolbar buttons which also invoke a URL popup; but the filename does not, and neither do some hoverable touchscreens.

Unfortunately, the only full remedy for this today is to avoid hover effects altogether on pages that wish to support both desktop and mobile devices—including thumbspage viewer pages as of 1.7. This is an unfair penalty on desktop browsers, where hovers work correctly, and are a common and even expected behavior. But it's also typical of the quality issues that mire the mobile realm, and one of far too many interoperability issues that plague the web domain. Alas, both seem to have been more accumulated than designed to date. For more on hover problems, see the note on this MDN page, and the iOS results for this search.

As a consolation, thumbspage viewer pages still provide some visual cues when a pointing device is used, by changing the cursor to a pointer whenever it covers any widget with a clickable action (though not when the action won't fire because JavaScript is disabled). This works well on all desktop browsers tested, but has no effect on mobile browsers even when a stylus is present—except for Samsung's browser on a stylus-equipped Galaxy smartphone, a device which otherwise inhabits a strange browser purgatory between mouse and touch, and further muddles a hover story that sorely needs a happier ending.

Improve thumbnail quality for shrunken images and GIFs (Mar-2020)

Summary: in thumbspage 1.7, thumbnails of non-transparent GIF images have been much improved, thanks to both a fix initially installed to do the same for thumbnails of shrunken images, and the generalized utility of the underlying Pillow library.

Per the usage note above, the site hosting thumbspage has started globally downscaling image filesize with the shrinkpix program. This makes online images both faster to view, and friendlier to limited-quota users. As a consequence of the conversions, though, thumbnails generated by thumbspage from shrunken images have potential to lose visual quality; in worst cases, the effect is a nearly complete munge.

To fix this, version 1.7 now internally and temporarily converts some images to "RGBA" color mode when creating their thumbnails. This produces thumbnails that look the same and are at least as good as those made from original, unshrunk images by prior versions. As a bonus, using "RGBA" for some unshrunk GIFs renders their thumbnails much better than before; to show how, here are two pages with GIF-image thumbs before and after the change.

For more technical details on this change, see the [1.7] notes in viewer-thumbs.py. This fix is not applied to JPEG images (which yield the same thumb quality when shrunk), or GIFs with transparencies (whose transparent parts would render black). An alternative "RGB" conversion was less constrained, but rendered Mac OS screenshot shadows fully black, which matched shadows on full-size viewer-page displays only coincidentally when the default black background color was used. These shadows may be best avoided as a rule, but this lesson comes too late for the hundreds of captures on thumbspage's website.

Retain—and update—Exif tags in rotated JPEG images (Mar-2020)

Summary: as of 1.7, thumbspage retains Exif tags in 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. 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: when Exif tags are propagated directly, they reflect the original image, not the rotated copy. Among the possible consequences, width/height dimension tags won'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 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 library used elsewhere in thumbspage must already be separately installed, but has almost no support for Exif tags, apart from fetching and saving their raw bytes data. 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 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 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.

Index pages: avoid text upscaling in iOS Safari landscape (May-2020)
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-2020)

Summary: in popup info dialogs triggered on filename taps, thumbspage 1.7 now labels capture dates for scanned images as "Digitized" instead of "Taken," as in this. This avoids potential confusion, but relies on device manufacturers (and programming libraries) to use Exif tags consistently.

The Exif standard's use of multiple date/time fields leaves roles somewhat vague, and relies on consistent interpretation by device manufacturers. In particular, the two main date/time tags—DateTimeOriginal and DateTimeDigitized—seem prone to interoperability skew. In practice, though, images captured on digital cameras normally have both their original and digitized tags set to capture (i.e., taken) date/time. While comparatively rare, scanned images instead generally have a blank original tag, and record scan date/time in the digitized tag. Exif defines an additional DateTime tag, but it's meant only for recording time changed, and is considered out of scope by thumbspage (and likely redundant with the file's modification time already shown).

In prior 1.7 releases, thumbspage's info popups used the original tag if present, else the digitized tag, and labeled the result "Taken" for either. This logic was borrowed from tagpix, which must select a filename prefix for uniqueness. It makes sense in thumbspage too if "Taken" is generalized to mean any digital origin, but might be mildly confusing for photo scans: the scan date/time doesn't reflect that of the actual scene, and most people are unlikely to manually add an Exif capture-date/time tag to older scanned images (when known at all). Here's how the prior release potentially puzzling display for a scan.

To do better, thumbspage's info screens now display either a "Taken" date/time, for images with an original-tag value; a "Digitized" date/time, for images with only a digitized-tag value; or "Taken: (unknown)" as before if neither tag is set (this is the normal case in thumbspage for PNG screenshots). Here's the new, improved results for a camera photo, a scanned photo, and a screenshot. This should better reflect photo scans in galleries—especially for photos with years in their filenames, like some at this site. This also accommodates screen-size limits in the info popup: displaying all date-related tags might run off screen on small mobiles, and date of origin is usually the main item of interest.

Beyond all this, it's also worth noting that Exif date/times have no time-zone information; 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.

iOS landscape: use JavaScript image scaling for all browsers (May-2020)

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 earlier 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 share was 3%, and the most popular iOS alternative weighed in at just 0.4% (and lower if analytics blockers were factored in); when time is limited, it's tough to justify investing it on outliers, especially when the only hope seems brittle kludges.

In retrospect, this policy may have been too broad. For one thing, not all sites' iOS percentages will be as low as those here. For another, most iOS issues were present only in Safari: only iOS Safari reported viewport sizes poorly, and only iOS Safari cropped landscape pages with mandatory toolbars. While Safari likely accounts for most iOS traffic on the web, 1.6's blanket policy unfairly penalized other iOS browsers whose landscape displays work well. The real clincher here, though, was a new option in iOS 13 which works around Safari's issues in full, and easily justifies a new approach.

To do better, thumbspage 1.7 by default now uses 1.6's JavaScript scaling for landscape views in all browsers on iOS, instead of the falling back on the former CSS-based scaling in this context. This is a clear win for all non-Safari iOS browsers tested; here are the results on a 4" test-device screen for Chrome, Firefox, the latest Edge, and UC Browser. These displays now better match those on Android, which has always scaled well in both orientations.

For iOS Safari, results are improved across the board, but vary by iOS release and user action. The best news is that thumbspage landscape views now work in full for users running iOS 13 and later. As of that version, iOS Safari has gained an option that hides its bottom toolbar altogether, and shrinks its top toolbar substantially. This option lives in the new "aA" menu at the upper left of Safari's window, and is applied to all pages opened in an enabling tab. For more usage details we'll skip here, try a web search; iOS 13 may also hide toolbars automatically when rotated to landscape, but the menu setting makes the effect permanent, and provides additional UI fixes. Specifically, when the new iOS 13 option is enabled:

All that being said, the thing about options is that they're optional. Although the new iOS 13 work-around can indeed solve multiple thumbspage issues in full, it's important to keep in mind that it won't help users who don't know about the new option; choose not to apply the new option; or use devices running iOS 12 and earlier that don't have the new option (yes, it's still possible). That's far short of an all-inclusive fix. Where the iOS 13 option isn't available or enabled, the new JavaScript scaling results in 1.7 are still obscured by toolbars and require a scroll, but they at least allow users to view image top and bottom more easily (the former CSS display revealed image bottom only during a downscroll).

Browser flux aside, thumbspage's new scaling rules are very simple: it now uses JavaScript image scaling everywhere, with just two exceptions. First, CSS-based scaling is still used as a fallback when JavaScript is disabled. Second, because this change differs from prior releases, and because some users may very well prefer the former results in Safaris not using the iOS 13 workaround, you can reinstate CSS-based landscape displays by setting the iOSSafariLandscapeCSS variable in the configs file. This legacy option works for iOS Safari only, because all other browsers handle JavaScript scaling without issue.

To summarize, thumbspage's complete image-page user story on mobile is now as follows:

At least that's the story until the next browser change. If web browsers are a kind of GUI, then web development is like having to make your code run on dozens of incompatible versions of a GUI toolkit, and on dozens of disparate platforms, all of which morph regularly and frequently and are naturally inclined to view interoperability as an obstacle to control. This has wrought a programming and maintenance nightmare—and ironically so, given the web's original portability ambitions. Please enjoy thumbspage today, but bear in mind that longevity cannot be counted among the assets of web-based systems.

Footnote: to be fair, iOS Safari isn't alone in harboring usability "features" that break websites. Edge for years had a URL-mouseover defect which rendered toolbar buttons unusable at the bottom left of a desktop page; and Chrome's duet adventures on Android regularly threaten to steal a sizable chunk of the display for browser agendas. Edge's overlay was fixed, and Chrome's duet is currently not a default, but both are typical of the sorts of vendor quirks that developers must battle constantly. A decent conspiracy theorist might point out here that vendors have vested interests in driving content to full-screen 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...

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 Mac OS, 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.

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 generated thumbnails to be right-side up (top edge on top), if they have valid "Orientation" Exif tags. 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 (JPEG and TIFF) from cameras and tools that tag as expected. More intrusively, this feature must rotate source-image files too (not just their thumbnails), because not all viewers will rotate images when opened from thumbnails. In thumbspage specifically, web browsers will not rotate tilted source images (except on "Raw" clicks), because its viewer pages display source images as in-page elements. The related PyPhoto program now also adjusts for orientation in memory only and does not require image copies, but uses forked thumbnail-generation code (see its website).

Because this feature modifies source images in-place, it by default saves them to backup copies with a .original extension before making changes. Users may also control the feature with two new settings in user_configs.py: autoRotateImages can turn the feature off, and backupRotatedImages can skip its .original backups. Disable rotation if needed or desired, and manually rotate tilted images before running thumbspage as preferred.

See examples/reorientation for a test case's results; that folder's restore-originals.py utility to restore from backups; and module viewer_thumbs.py for implementation code (rotation is neither automatic nor 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 grew more important with 1.7's new info dialogs which display date-taken tag data.

Other 1.6 Changes

In addition to the main items above, version 1.6 also:

  1. 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.

  2. Moves configuration options and viewer-page HTML to separate files for easier viewing and edits. See Customization above for links and story.

  3. 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.

  4. 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).

  5. 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.

  6. 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.

  7. 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).

  8. Works around a Pillow library bug that could occur only in limited usage contexts for folders having very many images. In brief, Pillow's auto-close of loaded image files does not work as documented, which can lead to "Too many open files" errors after many thumbnails have been generated. Here, this meant that results could reflect partial source content for very large folders, though only on Mac OS in general.

    The best and applied fix is to manually open and close image files, instead of passing filenames to Pillow. With this change, arbitrarily large folders are supported in all contexts. For more details on both the bug and its workaround, see module viewer_thumbs.py, where the fix is coded.

  9. 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 workaround—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.

  10. 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 workaround, 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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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).

  7. 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:

  1. Autoscrolls the thumbs table to appease mobile browsers.

  2. Adds padding to thumb cells to reduce run-together.

  3. Center-aligns thumbs images for a more even look; this helps overall, but especially for narrow/portrait images.

  4. 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 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:

  1. HTML-escapes all added text—image, folder, subfolder names.

  2. URL-escapes all added links—to thumbs, images, subfolders, and viewer pages in 1.5.

  3. 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.

  4. 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.

  5. Assumes any inserts are both HTML-safe and compatible with the default or configured inserts encoding. Be sure to verify your inserts before using this program's results.

See examples/unicode/images for the results of a comprehensive Unicode-content test case.

Note: if you use a custom HEADER.html file, make sure that the Unicode type declared in its content-type <meta> tag matches the setting for outputEncoding (#3 above). thumbspage uses the latter to save the whole index-page (including the content of its thumbnails table), so these two encodings must be the same or compatible.

Update: version 1.5 further refines URL escapes to use UTF-8 encoding for escape byte values, and loads its new viewer pages as UTF-8 by default (configurable as of 1.6 by setting templateEncoding; viewer pages are still output per outputEncoding). The Unicode test's folder also moved to the new path named above, and thumbspage development switched from Windows ("\") to Mac OS ("/") along the way, though some docs may still retain their former Windows bias.

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:

  1. Uses uniform-width columns, instead of sizing columns per content (on by default; see setting uniformColumns).

  2. 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.

  3. 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 is on by default, and skips folders whose names are prefixed with a _ or . character, as well as the generated thumbnails folder (whether it's named with a _ or . prefix or not).

Note: if enabled, the subfolder-links list is generated whether you use a custom HEADER.html file or not. When a custom header is used, the list appears after the header's text. Hence, pages that use a custom header should either accommodate the list in the header's opening content, or disable this feature and code the list manually as desired. Naturally, this matters only if there are subfolders in your images folder.

Tip: thumbspage must be run on each image subfolder in a tree separately (the tree is not walked automatically because its folders may have arbitrary content); and when viewing offline, index pages in subfolders must be clicked manually (online viewing normally opens image subfolder indexes automatically).

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 releases were spurred by using this program over time. Where thumbspage 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 most of them.

Open Issues

Like all software, thumbspage is an open-ended project. In closing, this section collects implementation-related issues that remain open in the latest release. It's likely of most interest to developers, though some usage context may also be gleaned along the way. Some of these items are also outstanding questions, open to your feedback.

Also note that this list may not be complete; search for "caveat" in code files for additional items, and see Version History above for more on version changes noted here.

TIFF images 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 techniques that are well beyond the scope of this project. Rarer image types are even more unlikely to be supportable (Safari initially displayed, but later crashed on, an index page with TIFFs, TGAs, and PBMs). Ultimately, this seems an inherent tradeoff for viewing images in browsers, and image conversion may be the best workaround. For more pointers, try a search.
Rotations drop Exif tags
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
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 workaround 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
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
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.
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.
Index-page columns
The number of columns on the generated index page might be dynamically configured in JavaScript from viewport size (much like the current dynamic scaling of view-page images), but this may be complex (e.g., font size would matter) and could be too jarring as windows are resized.
Image information display
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.

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.

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 closed-issues list and additional developer notes, in text file 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.

[Python Logo] Top Code Page Demo News Blog Apps Input ©M.Lutz