Scientific Computing

Lossless convert PNG image stack to PDF

The Python package img2pdf losslessly converts PNG images to PDF.

Install it with:

pip install img2pdf

The command line program “img2pdf” can do many tasks, but even more power comes from making a brief Python script. Here are simple command line examples.

Convert a single PNG to PDF:

img2pdf in.png -o out.pdf

Convert multiple PNG images to a single PDF:

img2pdf *.png -o joined.pdf

Rotate each page of a PDF from the command line

A common issue with document scanning is that pages may have been inserted in the wrong orientation, making the PDF difficult to read with upside-down text. This is trivially fixed with the QPDF command line tool.

qpdf --rotate=180 in.pdf out.pdf
in.pdf
input PDF file needing rotation out.pdf
output PDF file with rotated pages
--rotate=180
rotate each page 180 degrees

QPDF can do vastly more operations on PDF files as seen in the QPDF documentation. Poppler is oriented toward rendering and extracting content from PDF files rather than modifying them like QPDF, which does not render PDF files.

Install QPDF

  • Linux: apt install qpdf or similar
  • macOS: brew install qpdf
  • Windows: winget install QPDF.QPDF

On Windows, QPDF is installed under $Env:ProgramFiles/qpdf*/bin/qpdf.exe

Open same PDF multiple times

Opening the same PDF file multiple times is particularly useful when reviewing journal article drafts to view figures simultaneously with text describing the figure.

Okular is a free lightweight PDF viewer for Linux, macOS and Windows that trivially opens the same PDF multiple times by just selecting through the File → Open menu as many times as desired.

Okular is installed by:

  • Linux apt install okular or similar
  • macOS - see Okular download link above
  • Windows winget install KDE.Okular

CTest interactive debug mode

CTest –interactive-debug-mode is particularly useful on Windows to enable/disable debug dumps and error popup modal windows, especially for missing DLL errors. By default this feature is enabled. This default behavior to have interactive debug mode “on” can make problems with modal popups hanging on automated systems like CI or agentic workflows where user interaction is not desired or possible.

To avoid this, one can explicitly disable interactive debug mode by option

ctest --interactive-debug-mode=0

An even better approach is to make CMakePresets.json explicitly set the interactive debug mode for CTest invocation with preset or workflow with "interactiveDebugging": false thats inherited for all CTest preset execution like:

{
  "version": 6,

"testPresets": [
{
  "name": "default",
  "configurePreset": "default",
  "output": {
    "outputOnFailure": true,
    "verbosity": "verbose"
  },
  "execution": {
    "interactiveDebugging": false,
    "noTestsAction": "error",
    "scheduleRandom": true,
    "stopOnFailure": false
  }
}
]
}

In agentic instruction files like CMAKE.instructions.md or similar, consider adding a line like:

CTest on Windows should be run with option --interactive-debug-mode=0 to prevent modal popups from interrupting automated workflows.

NOTE: CTest environment variable CTEST_INTERACTIVE_DEBUG_MODE is read-only and has no effect on setting the interactive debug mode. The mode must be controlled via the --interactive-debug-mode command-line option when invoking CTest.


Related: MSVC exception handling popup disable

Disable Visual Studio exception modal window

Visual Studio executables built in Debug mode by default pop up modal debug windows if an unhandled exception occurs. This can be can be undesirable for reasons including:

  • users having to close nuisance windows manually.
  • CI runs hang if an exception occurs and the modal window appears.
  • AI agents or automated tools may hang with unexpected modal windows.
  • remotely operated systems: modal windows can be difficult to close if the modal window is accidentally off-screen.

Debug popups for exception handling can be disabled by C++ runtime library functions provided by Visual Studio described below. NOTE: missing DLL modal popups are fixed by the calling program such as CTest instead of this C++ method below.

Adding a few lines of code to the C++ program works around this issue by redirecting the error text to stderr console and not popping up the modal window.

This is also relevant to continuous integration systems such as GitHub Actions, which may hang with an unrealized modal dialog otherwise.

#ifdef _MSC_VER
#include <crtdbg.h>
#endif

int main(){
#ifdef _MSC_VER
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
#endif

// rest of program
}

CMake version recommendations and install

Downloading the latest release of CMake is usually easy. Admin / sudo is not required.

  • Linux: snap install cmake
  • macOS: brew install cmake
  • Windows: winget install Kitware.CMake
  • PyPI CMake package: python -m pip install cmake

For platforms where CMake binaries aren’t easily available, build CMake using scripts/build_cmake.cmake.

To see the merge requests for a certain release, use a URL like: https://gitlab.kitware.com/cmake/cmake/-/merge_requests?milestone_title=4.3.0&scope=all&state=all

Another CMake changelog summary we like.

CMake 4.5 additions include:

CMake 4.4 additions include:

CMake 4.3 additions include:

  • Common Package Specification (CPS) officially supported. This provides a solution for the long-standing program of build system-agnostic JSON package configuration files, which can be generated by CMake’s install(EXPORT) and consumable by other build systems.
  • cmake --version=json-v1 tells details of the libraries vendored into CMake, which is useful for debugging and reporting issues with CMake’s bundled dependencies. This is complementary to cmake -E capabilities which reports specific capabilities of the CMake.
  • CMake command line and file(ARCHIVE_CREATE) commands can specify compression level and algorithm in more detail
  • numerous generator string expressions were added

CMake 4.2 additions include:

  • Visual Studio 18 2026 generator added
  • cmake_language(TRACE) enables tracing just selected nestable portions of CMake script, which is important for debugging CMake projects due to the large volume of trace output.
  • Ability to force shorter paths on project builds, important for large or complex project on Windows where the 260 character path limit is a problem for some tools. This is done via CMAKE_INTERMEDIATE_DIR_STRATEGY which is a CMake environment variable as well as a CMake command-line option.
  • FASTBuild generator has impressive benchmarks. FASTBuild supports more modern platforms including Windows, versus ccache or distcc.
  • ExternalProject added options to modify environment variables at each step by CONFIGURE_ENVIRONMENT_MODIFICATION, BUILD_ENVIRONMENT_MODIFICATION, INSTALL_ENVIRONMENT_MODIFICATION, TEST_ENVIRONMENT_MODIFICATION
  • Target property SPDX_LICENSE

CMake 4.1 additions include:

  • project() added COMPAT_VERSION that propagates to subdirectories and can be queried for the top-level COMPAT_VERSION.

CMake 4.0 additions include:


Older CMake release changelogs:

Detect CMake non-target directives

CMake directives can be target-specific - generally starting with target*() like target_compile_options() versus non-target-specific like add_compile_options() or include_directories(). It’s usually recommended to use target-specific directives whenever possible, as they provide better scope control over individual targets, avoiding applying directives in unexpected scopes.

CMake provides a warning flag -Wnon-target-directive to detect such directives. Sometimes they may be truly unavoidable or in a third-party library out of one’s scope.

cmake -Werror=non-target-directive -B build

or to only warn without treating it as an error:

cmake -Wnon-target-directive -B build

Headless plotting and Wayland applications on Linux

On modern Linux installations, a program that saves plots to disk usually does not need a graphical desktop or display server. Matplotlib can render files with its non-interactive Agg backend.

Save plots without a display

Select Agg before importing pyplot, then save the figure:

import matplotlib
matplotlib.use("Agg")

import matplotlib.pyplot as plt

plt.plot([1, 2, 3], [1, 4, 9])
plt.savefig("plot.png")

Alternatively, select the backend when launching a script:

MPLBACKEND=Agg python plot.py

This is sufficient when the only graphical output needed is a saved image. No graphical desktop session is required.

Run an application that requires Wayland

Some GUI applications require a Wayland compositor even when no physical display is available. Install Weston, the reference Wayland compositor:

sudo apt install weston

Start its headless backend in one terminal:

weston --backend=headless --socket=wayland-headless

In another terminal running as the same user, launch the application with that socket:

WAYLAND_DISPLAY=wayland-headless application

Weston uses XDG_RUNTIME_DIR for its Wayland socket. Log-in sessions normally set this variable; if it is unset, use a properly owned, private runtime directory before starting Weston. Stop Weston with Ctrl+C when finished.

For applications that only need to save plots, prefer the application’s non-interactive plotting backend instead of starting a compositor. See the Matplotlib backend documentation and Weston’s running guide.

Terminal escape inter-program communications

Terminal escape sequences are often used to control the appearance and behavior of terminal emulators. Beyond visual effects, they can also facilitate inter-program communications by signaling events or passing information between programs running in the terminal. The OSC 7501 protocol of escape sequences allows programs to communicate their status to the terminal, which can then relay this information to other programs or scripts that are monitoring the terminal output. A prime use of this is for AI agents, but could also be used for coordinating other types of programs, such as build systems, monitoring tools, or custom terminal-based workflows. Check out the author’s article linked above for a detailed explanation and examples of how OSC 7501 can be used for inter-program communications in terminal environments.