{ "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", "\n", "https://en.wikipedia.org/wiki/Imposition\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Paged media: A timeline\n", "\n", "**1980s**\n", "\n", "[![](https://www.graphic-reseau.com/media/catalog/product/cache/3/small_image/170x170/9df78eab33525d08d6e5fb8d27136e95/l/o/logo-adobepostscript.png)](https://en.wikipedia.org/wiki/PostScript) [![](https://www.ghostscript.com/images/ghostscript_logo.png)](https://en.wikipedia.org/wiki/Ghostscript)\n", "\n", "\n", "**1990s**\n", "\n", "[![](https://web.archive.org/web/19970304211647im_/http://www3.adobe.com/GIFS/getacro.gif)](https://en.wikipedia.org/wiki/PDF) [![](https://pzwiki.wdka.nl/mw-mediadesign/images/f/f4/Mosaic.png)](https://en.wikipedia.org/wiki/HTML)\n", "\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", "\n", "**TODAY**\n", "\n", "![](https://upload.wikimedia.org/wikipedia/commons/thumb/6/61/HTML5_logo_and_wordmark.svg/180px-HTML5_logo_and_wordmark.svg.png) [![](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d5/CSS3_logo_and_wordmark.svg/180px-CSS3_logo_and_wordmark.svg.png)](https://drafts.csswg.org/css-page-3/) ![](https://upload.wikimedia.org/wikipedia/commons/thumb/7/7d/Adobe_PDF.svg/150px-Adobe_PDF.svg.png)\n", "\n", "https://en.wikipedia.org/wiki/CSS\n", "\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", "![](https://thedailykylie.files.wordpress.com/2013/03/tool-box.jpg)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Use the !shell\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..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!date" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!whoami" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!ls" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## An zine introduction to the terminal\n", "\n", "https://solarpunk.cool/zines/map-is-the-territory/\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## MAN oh MAN" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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": null, "metadata": {}, "outputs": [], "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "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": null, "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": null, "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", "* [pstops](pstops.pdf)\n", "* [pdfunite](pdfunite.pdf), [pdfseparate](pdfseparate.pdf)\n", "* pdftk\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", "\n", "* https://www.novell.com/documentation/suse91/suselinux-adminguide/html/ch06s08.html\n", "* https://wiki.scribus.net/canvas/How_to_make_impositions_with_pstops\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Make some **man**-uals with **ps2pdf**" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!man -t psnup | ps2pdf - man/psnup.pdf\n", "!man -t pstops | ps2pdf - man/pstops.pdf\n", "!man -t ps2pdf | ps2pdf - man/ps2pdf.pdf\n", "!man -t pdf2ps | ps2pdf - man/pdf2ps.pdf\n", "!man -t pdftk | ps2pdf - man/pdftk.pdf\n", "!man -t pdfposter | ps2pdf - man/pdfposter.pdf\n", "!man -t poster | ps2pdf - man/poster.pdf" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# 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." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pdfinfo txt/language.pdf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Back to the Future with **pdf2ps**\n", "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": null, "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": null, "metadata": {}, "outputs": [], "source": [ "!psnup -2 language.ps psnup.ps" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To look at it, run the ps2pdf..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!ps2pdf psnup.ps psnup.pdf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "AND LOOK HERE: [psnup.pdf](psnup.pdf)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "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": null, "metadata": {}, "outputs": [], "source": [ "!psnup -16 -c language.ps psnup.ps" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "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": null, "metadata": {}, "outputs": [], "source": [ "!pdf2ps 1.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": null, "metadata": {}, "outputs": [], "source": [ "!ps2pdf -sPAPERSIZE=a0 1.a0.ps 1.a0.pdf" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "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..." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pdf2ps 1.pdf - | pstops \"0@4.0\" -pa2 | ps2pdf -sPAPERSIZE=a0 - 1.a0.pdf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Make a blank PDF with **python** and reportlab.pdfgen.canvas" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from reportlab.pdfgen.canvas import Canvas\n", "from reportlab.lib.pagesizes import A4\n", "c = Canvas(\"blanka4.pdf\", pagesize=A4, bottomup=0)\n", "c.showPage()\n", "c.save()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tile and zoom with **poster**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the [poster](msn/poster.pdf) manual:\n", "\n", ">*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": null, "metadata": {}, "outputs": [], "source": [ "#!poster -mA3 -pA0 1.ps | ps2pdf - > poster.poster.pdf\n", "!poster -v -c0 -iA4 -mA4 -pA0 1.ps | ps2pdf - > poster.poster.pdf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The newer [pdfposter](man/pdfposter.pdf) works directly with PDF files, and has slightly different options.\n", "\n", "For example to prints an A4 input file on 8 A3 pages, forming an A0 poster:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!pdfposter -mA3 -pA0 1.pdf pdfposter.pdf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## **Puzzle**: Combine tools to make a poster with different elements\n", "![](https://upload.wikimedia.org/wikipedia/commons/thumb/4/48/15-Puzzle.jpg/330px-15-Puzzle.jpg)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.3" } }, "nbformat": 4, "nbformat_minor": 4 }