30

I am a relative TeX newbie trying to write a new package to generate timelines that can span multiple pages (the existing timeline solutions are not suitable for me, and I would like to write something superior to share with the community as way of 'giving back').

Anyway, at present I have successfully been able to mock up a very nice timeline using only the \begin{picture}...\end{picture} functionality (no tikz).

The problem is spanning pages - is it possible to have TeX automatically break a figure at certain points in order to support horizontal or vertical spanning of multiple pages?

Display Name
  • 46,933
  • no, that is not possible –  Jul 12 '11 at 08:08
  • 3
    I don't have a complete answer, but here is another idea: how about having a single pdf document with your picture as one single huge page, then crop and split the pdf as needed with an external tool, and finally import back the pieces in your final document with plain \includegraphic’s? – Juan A. Navarro Jul 12 '11 at 10:05
  • @Juan: Wouldn't that be exactly what I have in my answer, but just going over an external image? Note that gincltex lets you include .tex files like images using \includegraphics. It's code is very similar to adjustbox which allows direct clipping of TeX material. – Martin Scharrer Jul 12 '11 at 10:29

3 Answers3

38

Unfortunately this doesn't work. Picture environments (picture, tikzpicture, ...) are (horizontal) boxes on their own and LaTeX doesn't break these. You would need to do this by yourself.

If you are able to detect it once your picture is too long (longer than max. \textheight or calculate the rest of the current page) you could insert some code like <global save settings>\end{picture}\begin{picture}<restore settings> to close the current picture and open a new one. I do similar things with TikZ's \path in tikz-timing where I have to start a new path to change the colour and style but want to keep certain settings and the position. It's, however, a little tricky, especially for whole pictures, I guess.

Another possibility would be to draw the whole picture as long it turns out to be, but inside a savebox, i.e. you store it first but don't typeset it directly. Then you can measure its height (\ht\yourboxmacro) and if it is too high you can clip it using my adjustbox package. The idea would be to insert it twice: once clipped to the size of the first page and then again on the next page with the first part clipped. Using a loop you could, of course, support more than two pages.

Here some example code. I used tikzpicture here because I know its syntax but not the one of picture very well. It will work with that as well of course. (Note that there is a bug in the current version of adjustbox. I fixed it for the example image below, but the top is still clipped incorrectly. I have to have a closer look at it. However, the basic idea is sound and works.)

\documentclass{article}

\usepackage{tikz}
\usepackage{adjustbox}

\newsavebox{\mysavebox}
\newlength{\myrest}
\begin{document}

\begin{lrbox}{\mysavebox}%
\begin{tikzpicture}[red,thick]
 \draw (0,0) rectangle (-.9\textwidth,-2.8\textheight);
 \draw (0,0) -- (-.9\textwidth,-2.8\textheight);
 \draw (-.9\textwidth,0) -- (0,-2.8\textheight);
 \path (-1mm,-1mm);
 \path (current bounding box.north east) +(1mm,1mm);
\end{tikzpicture}%
\end{lrbox}%
%
\ifdim\ht\mysavebox>\textheight
    \setlength{\myrest}{\ht\mysavebox}%
    \loop\ifdim\myrest>\textheight
        \newpage\par\noindent
        \clipbox{0 {\myrest-\textheight} 0 {\ht\mysavebox-\myrest}}{\usebox{\mysavebox}}%
        \addtolength{\myrest}{-\textheight}%
    \repeat
    \newpage\par\noindent
    \clipbox{0 0 0 {\ht\mysavebox-\myrest}}{\usebox{\mysavebox}}%
\else
    \usebox{\mysavebox}%
\fi

\end{document}

Result

Cornul11
  • 125
Martin Scharrer
  • 262,582
  • 2
    agreed that (with currently available software) it's not possible to produce a picture that breaks across page boundaries. however, if the picture were put in a box, bits of it could surely be snipped off and put on different pages. (i think this was proposed, long ago, as a means of doing multi-page tables before supertabular and longtable, etc., came along; since the proposal would presumably have appeared in the '80s, i'm not particularly surprised i didn't remember the details...) – wasteofspace Jul 12 '11 at 10:10
  • Thanks everyone. Actually I discovered and tested this approach on my own after posting, by modifying some code I found in a recent post on comp.text.tex (https://groups.google.com/forum/#!topic/comp.text.tex/se8qapxOdew). However, I hadn't yet discovered that it's possible to include raw TeX code instead of an image, nor discovered the loop syntax. Thanks Martin. Also thanks for the history insight Anon ... I do appreciate history, being that I'm attempting to write a timeline package for a history book and all! – Dr. Voltaire Jul 12 '11 at 11:00
  • 2
    @Dr.Voltaire: If clipping is new for you see the grfguide (manual of graphics/x) for more information. My adjustbox package is based on that and extends it to TeX code. – Martin Scharrer Jul 12 '11 at 11:18
  • @MartinScharrer What could you propose if the picture spanned both horisontally and vertically? With your above method, a picture spanning 2 pages vertically and many pages horisontally produces just 2 pages. – ajeh Jun 18 '14 at 16:09
  • @ajeh I think you can just include further \newpage and \clipbox commands, clipping the appropriate parts of the pictures appropriately. Take a look at the documentation for the command and compare it with the answer. Then try to adapt it to add the two pages for the top/bottom of the picture after/before the bottom/top of the picture. – cfr Jul 06 '14 at 19:33
  • @MartinScharrer: If I add a paragraph of text before \begin{lrbox}, this will cause the picture to start on the next page leaving the rest of current page blank. Is there a way to adjust the picture to start under the paragraph on the same page? – Sebastian Widz Mar 03 '17 at 19:04
  • @SebastianWidz: My answer is for the case when the full page should be used, therefore it checks the height against \textheight. If there is already some content on the page the included image portion is to high and then moved to the next page. If you only want to use the remaining part of the page then this is also possible but more difficult (i.e. needs an anchor and at least two compiler runs). There are already questions and answers about that, so try the search. You can also post a separate follow-up question (just select "Ask Question" and reference the original question or my answer). – Martin Scharrer Mar 15 '17 at 21:40
  • @MartinScharrer is there still a bug in adjustbox? You might want to remove your note, if not. – user1 Apr 19 '19 at 00:40
16

I know this is a place for TeX, but I ran into the problem and wrote a bit of Python to solve it for me. It requires Python and PIL (the Python Imaging Library)

Say you have a really long picture like this, and you want to slice it up into smaller bits, because it is so long.

Picture

Here is a Python script that will do that.

from __future__ import division
from PIL import Image
import math
import os

def long_slice(image_path, out_name, outdir, slice_size): """slice an image into parts slice_size tall""" img = Image.open(image_path) width, height = img.size upper = 0 left = 0 slices = int(math.ceil(height/slice_size))

count = 1
for slice in range(slices):
    #if we are at the end, set the lower bound to be the bottom of the image
    if count == slices:
        lower = height
    else:
        lower = int(count * slice_size)  

    bbox = (left, upper, width, lower)
    working_slice = img.crop(bbox)
    upper += slice_size
    #save the slice
    working_slice.save(os.path.join(outdir, &quot;slice_&quot; + out_name + &quot;_&quot; + str(count)+&quot;.png&quot;))
    count +=1

if name == 'main':

long_slice(&quot;longcat.jpg&quot;, #image filename 
           &quot;longcat&quot;, #slice names
            os.getcwd(), #output dir
            300 #height in px
           )

This is is the output

Picture


Picture


Picture

Gourneau
  • 269
5

I really liked Martin Scharrer's answer (from July 12, 2011) and his example works. However, when I tried modifying Martin's answer to use for my purposes I found the code clipped \mysavebox in a strange way (Perhaps it was because I needed to use a minipage). It took me 2 months of experimenting and searching online to find the reason why. The total vertical length of \mysavebox includes not only the height, but also the depth (i.e., \ht\mysavebox + \dp\mysavebox). Now the clipping for each page works great. If you are finding similar difficulties, maybe the following solution will help you. I use the new variable I create, \mytotallen, like Martin uses \ht\mysavebox.

\documentclass{article}

\usepackage{tikz}
\usepackage{adjustbox}

\newsavebox{\mysavebox}
\newlength{\myrest}
\begin{document}

\begin{lrbox}{\mysavebox}
  \begin{minipage}{\textwidth}
    \begin{footnotesize} %change font size
      \begin{spacing}{0.5}
        Lots and lots and lots of text... (3 pages worth)
      \end{spacing}
    \end{footnotesize}
  \end{minipage}
\end{lrbox}

\newlength{\mytotallen} %create a new length variable \mytotallen
\setlength{\mytotallen}{\ht\mysavebox + \dp\mysavebox} %add together the height (\ht) and depth (\dp) of \mysavebox
\ifdim\mytotallen>\textheight
    \setlength{\myrest}{\mytotallen} %initialize \myrest
    \loop\ifdim\myrest>\textheight
        \newpage\par\noindent
        \clipbox{0 {\myrest-\textheight} 0 {\mytotallen-\myrest}}{\usebox{\mysavebox}}
        \addtolength{\myrest}{-\textheight}%subtract one page's length each loop from \myrest
    \repeat
    \newpage\par\noindent
    \clipbox{0 0 0 {\mytotallen-\myrest}}{\usebox{\mysavebox}}
\else
    \usebox{\mysavebox}
\fi
\end{document}