Skip to content

Instantly share code, notes, and snippets.

@eevmanu
Created March 26, 2025 17:58
Show Gist options
  • Save eevmanu/a72b1507057984623d7e0f80f8068bf8 to your computer and use it in GitHub Desktop.
Save eevmanu/a72b1507057984623d7e0f80f8068bf8 to your computer and use it in GitHub Desktop.
`gemini-2.5-pro-exp-03-25` attempt to use ghostscript `gs` to create a dark mode version of an input pdf file

Okay, let's break this down.

Yes, it is possible to achieve a color inversion effect in Ghostscript, similar to the invert(100%) part of your CSS filter. Replicating the hue-rotate(180deg) part exactly is significantly more complex within Ghostscript's standard processing model, but the simple inversion often gets you 90% of the way to a usable "dark mode".

The core idea is to use PostScript's transfer functions. These functions modify color component values just before they are rendered. To invert a color component c (where c is between 0 and 1), the function is simply 1 - c.

We need to apply this function to all relevant color spaces commonly found in PDFs: DeviceGray, DeviceRGB, and DeviceCMYK.

Here is the Ghostscript command you can use in a Bash shell script:

#!/bin/bash

# Input PDF file
input_pdf="input.pdf"

# Output PDF file (dark mode)
output_pdf="output_dark.pdf"

# Check if input file exists
if [[ ! -f "$input_pdf" ]]; then
  echo "Error: Input file '$input_pdf' not found."
  exit 1
fi

echo "Applying color inversion to '$input_pdf' -> '$output_pdf'..."

# Ghostscript command
gs \
  -o "$output_pdf" \
  -sDEVICE=pdfwrite \
  -dPDFSETTINGS=/default \
  -dNOPAUSE \
  -dBATCH \
  -c '<</TransferFunction {1 exch sub} /TransferFunctionRGB [ {1 exch sub} {1 exch sub} {1 exch sub} ] /TransferFunctionCMYK [ {1 exch sub} {1 exch sub} {1 exch sub} {1 exch sub} ]>> setpagedevice' \
  -f "$input_pdf"

# Check if Ghostscript was successful
if [[ $? -eq 0 ]]; then
  echo "Successfully created '$output_pdf'."
else
  echo "Error during Ghostscript processing."
  # Optional: remove potentially incomplete output file
  # rm -f "$output_pdf"
  exit 1
fi

exit 0

Explanation:

  1. #!/bin/bash: Shebang line for Bash scripts.
  2. input_pdf="...", output_pdf="...": Variables for filenames.
  3. if [[ ! -f ... ]]: Basic check to ensure the input file exists.
  4. gs: The Ghostscript command.
  5. -o "$output_pdf": Specifies the output file name.
  6. -sDEVICE=pdfwrite: Tells Ghostscript to output a PDF file. This device re-interprets and re-generates the PDF.
  7. -dPDFSETTINGS=/default: A general preset for compatibility and size. You could try /printer or /prepress for potentially higher quality if needed.
  8. -dNOPAUSE -dBATCH: Essential for running Ghostscript non-interactively in a script.
  9. -c '...': Executes the following PostScript code string.
    • << ... >> setpagedevice: This is the PostScript syntax for setting page device parameters. We are modifying how colors are handled for the entire page.
    • /TransferFunction {1 exch sub}: Sets the transfer function for DeviceGray color space. {1 exch sub} is PostScript code: it takes the input gray value (which is left on the stack), pushes 1, swaps them (exch), and subtracts (sub), effectively calculating 1 - input_gray.
    • /TransferFunctionRGB [ {1 exch sub} {1 exch sub} {1 exch sub} ]: Sets the transfer functions for DeviceRGB. It takes an array of three procedures, one for Red, one for Green, one for Blue. We use the same inversion logic for all three.
    • /TransferFunctionCMYK [ {1 exch sub} {1 exch sub} {1 exch sub} {1 exch sub} ]: Sets the transfer functions for DeviceCMYK, applying the inversion to Cyan, Magenta, Yellow, and Black components.
  10. -f "$input_pdf": Specifies the input PDF file. Crucially, the -c code runs before Ghostscript starts processing the pages from this file, setting up the color inversion environment.
  11. if [[ $? -eq 0 ]]: Checks the exit status of the gs command to report success or failure.

Limitations & Considerations:

  1. Hue Rotation: This command only performs the invert(100%). Replicating hue-rotate(180deg) would require intercepting and transforming every color specification (e.g., via setrgbcolor, setcmykcolor, image data, etc.) through a color space conversion (like RGB -> HSL -> modify H -> HSL -> RGB), which is extremely complex and fragile to implement robustly in Ghostscript for arbitrary PDFs. The simple inversion is usually sufficient for a dark mode effect.
  2. Color Spaces: This handles the most common device color spaces. PDFs using ICC profiles, Indexed colors, or Separation colors might behave differently or not be inverted correctly.
  3. Transparency & Blend Modes: Complex transparency and blend modes might produce unexpected results after inversion.
  4. Images vs. Vectors: The inversion applies universally, affecting both vector elements (text, lines) and raster images.
  5. Quality/Fidelity: pdfwrite re-generates the PDF. While generally good, it's not guaranteed to be a 100% perfect replica of the original structure, only the visual appearance (as modified by the transfer functions).

Save the script (e.g., pdf_darkmode.sh), make it executable (chmod +x pdf_darkmode.sh), place your input PDF as input.pdf in the same directory, and run it (./pdf_darkmode.sh). It will create output_dark.pdf.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment