This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# The Ghostscript Imposition\n",
"\n",
"> Imposition is one of the fundamental steps in the prepress printing process. It consists of the arrangement of the printed product’s pages on the printer’s sheet, in order to obtain faster printing, simplify binding and reduce paper waste.\n",
"\n",
"> Correct imposition minimizes printing time by maximizing the number of pages per impression, reducing cost of press time and materials. To achieve this, the printed sheet must be filled as fully as possible. \n",
"[Dave Raggett's Touch of Style tutorial](https://www.w3.org/MarkUp/Guide/Style) from/updated 2002, is a good introduction to and reflection on using CSS for the web -- notice there's almost no discussion of print -- it's all about how pages get rendered online, aka in a web browser. This is still the focus of CSS today... but...\n",
"CSS3 includes an extensive [Paged Media Specification](https://drafts.csswg.org/css-page-3/) describing how CSS can be *also* used to render \"paged media\" (ie when printed or saved as PDF). Most browsers do not implement these rules (except in some cases when you print a page). The *Weasyprint* python library implements some of these guidelines."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## The \"UNIX\" philosophy of small tools composed together (and the pipeline)\n",
"Jupyter (and ipython before it) supports the use of shell commands fluidly along side your python code. When the line starts with a **!** (the exclamation point, often called **bang** by command line users), the command is interpreted as a shell command and performed, the results displayed below, just like with python code. Note that the shell has different rules about what a good structure is. In any case the first word is the name of a command..."
"A very important concept from the UNIX / Linux / Libre software world is that documentation ought to be seen as an full part of the distribution of software. When software is installed, it often installs a so-called \"man page\" (for manual). You can then read the manual with the \"man\" command followed by the command you are interested in..."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GS(1) Ghostscript GS(1)\n",
"\n",
"NAME\n",
" gs - Ghostscript (PostScript and PDF language interpreter and pre‐\n",
" viewer)\n",
"\n",
"SYNOPSIS\n",
" gs [ options ] [ files ] ...\n",
"\n",
"DESCRIPTION\n",
" The gs command invokes Ghostscript, an interpreter of Adobe Systems'\n",
" PostScript(tm) and Portable Document Format (PDF) languages. gs reads\n",
" \"files\" in sequence and executes them as Ghostscript programs. After\n",
" doing this, it reads further input from the standard input stream (nor‐\n",
" mally the keyboard), interpreting each line separately and output to an\n",
" output device (may be a file or an X11 window preview, see below). The\n",
" interpreter exits gracefully when it encounters the \"quit\" command (ei‐\n",
" ther in a file or from the keyboard), at end-of-file, or at an inter‐\n",
" rupt signal (such as Control-C at the keyboard).\n",
"\n",
" The interpreter recognizes many option switches, some of which are de‐\n",
" scribed below. Please see the usage documentation for complete informa‐\n",
" tion. Switches may appear anywhere in the command line and apply to all\n",
" files thereafter. Invoking Ghostscript with the -h or -? switch pro‐\n",
" duces a message which shows several useful switches, all the devices\n",
" known to that executable, and the search path for fonts; on Unix it\n",
" also shows the location of detailed documentation.\n",
"\n",
" Ghostscript may be built to use many different output devices. To see\n",
" which devices your executable includes, run \"gs -h\".\n",
"\n",
" Unless you specify a particular device, Ghostscript normally opens the\n",
" first one of those and directs output to it.\n",
"\n",
" If you have installed the ghostscript-x Debian package and are under X,\n",
" the default device is an X11 window (previewer), else ghostscript will\n",
" use the bbox device and print on stdout the dimension of the postscript\n",
" file.\n",
"\n",
" So if the first one in the list is the one you want to use, just issue\n",
" the command\n",
"\n",
" gs myfile.ps\n",
"\n",
" You can also check the set of available devices from within Ghost‐\n",
" script: invoke Ghostscript and type\n",
"\n",
" devicenames ==\n",
"\n",
" but the first device on the resulting list may not be the default de‐\n",
" vice you determine with \"gs -h\". To specify \"AbcXyz\" as the initial\n",
" output device, include the switch\n",
"\n",
" -sDEVICE=AbcXyz\n",
"\n",
" For example, for output to an Epson printer you might use the command\n",
"\n",
" gs -sDEVICE=epson myfile.ps\n",
"\n",
" The \"-sDEVICE=\" switch must precede the first mention of a file to\n",
" print, and only the switch's first use has any effect.\n",
"\n",
" Finally, you can specify a default device in the environment variable\n",
" GS_DEVICE. The order of precedence for these alternatives from highest\n",
" to lowest (Ghostscript uses the device defined highest in the list) is:\n",
"\n",
" Some devices can support different resolutions (densities). To specify\n",
" the resolution on such a printer, use the \"-r\" switch:\n",
"\n",
" gs -sDEVICE=<device> -r<xres>x<yres>\n",
"\n",
" For example, on a 9-pin Epson-compatible printer, you get the lowest-\n",
" density (fastest) mode with\n",
"\n",
" gs -sDEVICE=epson -r60x72\n",
"\n",
" and the highest-density (best output quality) mode with\n",
"\n",
" gs -sDEVICE=epson -r240x72.\n",
"\n",
" If you select a printer as the output device, Ghostscript also allows\n",
" you to choose where Ghostscript sends the output -- on Unix systems,\n",
" usually to a temporary file. To send the output to a file \"foo.xyz\",\n",
" use the switch\n",
"\n",
" -sOutputFile=foo.xyz\n",
"\n",
" You might want to print each page separately. To do this, send the\n",
" output to a series of files \"foo1.xyz, foo2.xyz, ...\" using the \"-sOut‐\n",
" putFile=\" switch with \"%d\" in a filename template:\n",
"\n",
" -sOutputFile=foo%d.xyz\n",
"\n",
" Each resulting file receives one page of output, and the files are num‐\n",
" bered in sequence. \"%d\" is a printf format specification; you can also\n",
" use a variant like \"%02d\".\n",
"\n",
" You can also send output to a pipe. For example, to pipe output to the\n",
" \"lpr\" command (which, on many Unix systems, directs it to a printer),\n",
" use the option\n",
"\n",
" -sOutputFile=%pipe%lpr\n",
"\n",
" You can also send output to standard output:\n",
"\n",
" -sOutputFile=-\n",
" or\n",
" -sOutputFile=%stdout%\n",
"\n",
" In this case you must also use the -q switch, to prevent Ghostscript\n",
" from writing messages to standard output.\n",
"\n",
" To select a specific paper size, use the command line switch\n",
"\n",
" -sPAPERSIZE=<paper_size>\n",
"\n",
" for instance\n",
"\n",
" -sPAPERSIZE=a4\n",
" or\n",
" -sPAPERSIZE=legal\n",
"\n",
" Most ISO and US paper sizes are recognized. See the usage documentation\n",
" for a full list, or the definitions in the initialization file\n",
" \"gs_statd.ps\".\n",
"\n",
" Ghostscript can do many things other than print or view PostScript and\n",
" PDF files. For example, if you want to know the bounding box of a\n",
" PostScript (or EPS) file, Ghostscript provides a special \"device\" that\n",
" just prints out this information.\n",
"\n",
" For example, using one of the example files distributed with Ghost‐\n",
" Diverse document files (may need to install ghostscript-doc\n",
" package)\n",
"\n",
"INITIALIZATION FILES\n",
" When looking for the initialization files \"gs_*.ps\", the files related\n",
" to fonts, or the file for the \"run\" operator, Ghostscript first tries\n",
" to open the file with the name as given, using the current working di‐\n",
" rectory if no directory is specified. If this fails, and the file name\n",
" doesn't specify an explicit directory or drive (for instance, doesn't\n",
" contain \"/\" on Unix systems), Ghostscript tries directories in this or‐\n",
" der:\n",
"\n",
" 1. the directories specified by the -I switches in the command line\n",
" (see below), if any;\n",
"\n",
" 2. the directories specified by the GS_LIB environment variable, if\n",
" any;\n",
"\n",
" 3. the directories specified by the GS_LIB_DEFAULT macro in the Ghost‐\n",
" script makefile when the executable was built. GS_LIB_DEFAULT is\n",
" \"/usr/share/ghostscript/[0-9]*.[0-9]*/lib\" on a Debian system where\n",
" \"[0-9]*.[0-9]*\" represents the Ghostscript version number\n",
"\n",
" Each of these (GS_LIB_DEFAULT, GS_LIB, and -I parameter) may be either\n",
" a single directory or a list of directories separated by \":\".\n",
"\n",
"ENVIRONMENT\n",
" GS_OPTIONS\n",
" String of options to be processed before the command line op‐\n",
" tions\n",
"\n",
" GS_DEVICE\n",
" Used to specify an output device\n",
"\n",
" GS_FONTPATH\n",
" Path names used to search for fonts\n",
"\n",
" GS_LIB Path names for initialization files and fonts\n",
"\n",
" TEMP Where temporary files are made\n",
"\n",
"X RESOURCES\n",
" Ghostscript, or more properly the X11 display device, looks for the\n",
" following resources under the program name \"Ghostscript\":\n",
"\n",
" borderWidth\n",
" The border width in pixels (default = 1).\n",
"\n",
" borderColor\n",
" The name of the border color (default = black).\n",
"\n",
" geometry\n",
" The window size and placement, WxH+X+Y (default is NULL).\n",
"\n",
" xResolution\n",
" The number of x pixels per inch (default is computed from\n",
" WidthOfScreen and WidthMMOfScreen).\n",
"\n",
" yResolution\n",
" The number of y pixels per inch (default is computed from\n",
" HeightOfScreen and HeightMMOfScreen).\n",
"\n",
" useBackingPixmap\n",
" Determines whether backing store is to be used for saving dis‐\n",
" play window (default = true).\n",
"\n",
" See the usage document for a more complete list of resources. To set\n",
" these resources on Unix, put them in a file such as \"~/.Xresources\" in\n",
" the following form:\n",
"\n",
" Ghostscript*geometry: 612x792-0+0\n",
" Ghostscript*xResolution: 72\n",
" Ghostscript*yResolution: 72\n",
"\n",
" Then merge these resources into the X server's resource database:\n",
"\n",
" % xrdb -merge ~/.Xresources\n",
"\n",
"SEE ALSO\n",
" The various Ghostscript document files (above), especially Use.htm. On\n",
" Debian you may need to install ghostscript-doc before reading the docu‐\n",
" mentation.\n",
"\n",
"BUGS\n",
" See http://bugs.ghostscript.com/ and the Usenet news group\n",
" comp.lang.postscript.\n",
"\n",
"VERSION\n",
" This document was last revised for Ghostscript version 9.27.\n",
"\n",
"AUTHOR\n",
" Artifex Software, Inc. are the primary maintainers of Ghostscript.\n",
" Russell J. Lang, gsview at ghostgum.com.au, is the author of most of\n",
" the MS Windows code in Ghostscript.\n",
"\n",
"9.27 4 April 2019 GS(1)\n"
]
}
],
"source": [
"!man gs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you read the manual on the man command itself (type man man)... You see it supports different output formats. The -t option outputs in the Postscript language."
"(as last re)-.1 E(vised for Ghostscript v)-.25 E(ersion 9.27.)-.15 E F1\n",
"-.548(AU)72 112.8 S(THOR).548 E F0(Artife)108 124.8 Q 4.865(xS)-.15 G\n",
"(oftw)-4.865 E 2.366\n",
"(are, Inc. are the primary maintainers of Ghostscript.)-.1 F 2.366\n",
"(Russell J. Lang, gsvie)7.366 F 4.866(wa)-.25 G 4.866(tg)-4.866 G(host-)\n",
"-4.866 E(gum.com.au, is the author of most of the MS W)108 136.8 Q(indo)\n",
"-.4 E(ws code in Ghostscript.)-.25 E 188.445(9.27 4)72 768 R(April 2019)\n",
"2.5 E(6)203.445 E 0 Cg EP\n",
"%%Trailer\n",
"end\n",
"%%EOF\n"
]
}
],
"source": [
"!man -t gs"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Not very pretty, luckily we have **ghostscript** which is the basis of many smaller commands, such as one to convert postscript to pdf. The command is called **ps2pdf**. When can connect the two commands together in so called **pipeline** with the **|** character -- which is called the *pipe* because of this usage."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"First let's make a folder for our manuals"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"!mkdir -p man"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now let's run man gs and pipe the output to ps2pdf, saving in the file [man/gs.pdf](man/gs.pdf)."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
"!man -t gs | ps2pdf - man/gs.pdf"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## A PDF Toolkit\n",
"\n",
"* python: ReportLab (more for *generation* not pre-existing PDF)\n",
"* [gs](gs.pdf)\n",
"* [ps2pdf](ps2pdf.pdf), [pdf2ps](pdf2ps.pdf)\n",
"* [psnup](psnup.pdf), and [poster](poster.pdf)/[pdfposter](pdfposter.pdf)\n",
"* python: [pikepdf](https://github.com/pikepdf/pikepdf) *active project* with [docs](https://pikepdf.readthedocs.io/en/latest/index.html) and a fish logo\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Some links that were used in developing some of this notebook:\n",
"# NB These tools are often part of other packages of tools, for instanced:\n",
"# apt install ghostscript psutils pdfposter"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Read the label with **pdfinfo**\n",
"PDFs have useful info like number of pages and the (default) page size, but also can contain various *metadata* like Title, Keywords, and Author, and the \"Producer\" which often indicates what software was used to make the file."
"Many of the commands discussed here have their origins in the 1990s and were written to work with Postscript. Luckily there's also a **pdf2ps** command to go from PDF to Postscript. This command outputs (unless otherwise told) in a file with the same name but extension .ps\n"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"!pdf2ps txt/language.pdf"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[language.ps](language.ps)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## **psnup** saves trees"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1] [2] Wrote 2 pages, 481771 bytes\n"
]
}
],
"source": [
"!psnup -2 language.ps psnup.ps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To look at it, run the ps2pdf..."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"DEBUG: FC_WEIGHT didn't match\n"
]
}
],
"source": [
"!ps2pdf psnup.ps psnup.pdf"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"AND LOOK HERE: [psnup.pdf](psnup.pdf)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1] [2] Wrote 2 pages, 481777 bytes\n"
]
}
],
"source": [
"!psnup -2 -p a3 -s a3 language.ps psnup.ps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Repeat the steps above to see it (make sure you close the PDF to reload it).\n",
"\n",
"The -c option lays out in column order (instead of rows). Check out the [manual](man/psnup.pdf)."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1] Wrote 1 pages, 485929 bytes\n"
]
}
],
"source": [
"!psnup -16 -c language.ps psnup.ps"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1] Wrote 1 pages, 485997 bytes\n"
]
}
],
"source": [
"!psnup -16 -c -p a0 language.ps psnup.ps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## **pdftk** is another PDF toolkit\n",
"This command can do many things. Let's use it to extract a page"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"!pdftk A=txt/language.pdf cat A1 output 1.pdf"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Scale (among other things) with the multi-faceted **pstops**"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1] [2] [3] [4] Wrote 4 pages, 481656 bytes\n",
"[1] [2] [3] [4] Wrote 4 pages, 481656 bytes\n"
]
}
],
"source": [
"!pdf2ps txt/language.pdf 1.ps\n",
"# Scale up A4 to A2, A0\n",
"!pstops \"0@2.0\" -pa2 1.ps 1.output.ps\n",
"!pstops \"0@4.0\" -pa0 1.ps 1.a0.ps"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"NB: When subsequently using **ps2pdf** on a resized postscript file, you should explictly specify the output paper size:"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"DEBUG: FC_WEIGHT didn't match\n"
]
}
],
"source": [
"!ps2pdf -sPAPERSIZE=a0 1.a0.ps 1.a0.pdf"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Creator: GPL Ghostscript 927 (ps2write)\n",
"Producer: GPL Ghostscript 9.27\n",
"CreationDate: Wed Oct 28 21:23:58 2020 CET\n",
"ModDate: Wed Oct 28 21:23:58 2020 CET\n",
"Tagged: no\n",
"UserProperties: no\n",
"Suspects: no\n",
"Form: none\n",
"JavaScript: no\n",
"Pages: 4\n",
"Encrypted: no\n",
"Page size: 2384 x 3370 pts (A0)\n",
"Page rot: 0\n",
"File size: 63970 bytes\n",
"Optimized: no\n",
"PDF version: 1.4\n"
]
}
],
"source": [
"!pdfinfo 1.a0.pdf"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## From pdf to ps and back again with a **pipeline sandwich**\n",
"You might say, what a drag that pstops only scales postscript files and not PDF, well just wrap it in a Pipeline sandwich..."
">*Poster* can be used to create a large poster by building it from multiple pages and/or printing it on large me-dia. Itexpects as input a generic (encapsulated) postscript file, normally printing on a single page.Theoutput is again a postscript file, maybe containing multiple pages together building the poster.The outputpages bear cutmarks and have slightly overlapping images for easier assembling. The input picture will bescaled to obtain the desired size"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Using default whitemargin of 0\n",
"Deciding for 4 columns and 4 rows of portrait pages.\n",