71

How do I easily compile only a page range or page selection? That is, I would like a document that

  • looks exactly like the full document,
  • has only the subset of pages that were manually chosen, by page number (this can be one contiguous range or a more complicated selection, like "12-15,23,45-49"),
  • has correct page numbers, and
  • has correct links, except links to missing pages should be either dysfunctional or absent.
2
  • 2
    So \input, \includeonly type of solutions are not allowed right? I mean like this tex.stackexchange.com/questions/3462/…
    – percusse
    Feb 1, 2013 at 0:28
  • 1
    @percuße I would say they are not allowed because I would really like to specify page ranges. In any case, thanks for linking to this! Feb 1, 2013 at 14:28

7 Answers 7

36

atbegshi provides the capability to discard a ready-to-be-shipped-out page using \AtBeginShipoutDiscard. Together with etoolbox's list-processing capability, you can easily discard/retain pages conditionally:

\documentclass{article}
\usepackage{lipsum,atbegshi,etoolbox}% http://ctan.org/pkg/{lipsum,atbegshi,etoolbox}
\makeatletter
\newcommand{\discardpages}[1]{% \discardpages{<csv list>}
  \xdef\discard@pages{#1}% Store pages to discard
  \AtBeginShipout{% At shipout, decide whether to discard page/not
    \renewcommand*{\do}[1]{% How to handle each page entry in csv list
      \ifnum\value{page}=##1\relax%
        \AtBeginShipoutDiscard% Discard page/not
        \gdef\do####1{}% Do nothing further
      \fi%
    }%
    \expandafter\docsvlist\expandafter{\discard@pages}% Process list of pages to discard
  }%
}
\newif\ifkeeppage
\newcommand{\keeppages}[1]{% \keeppages{<csv list>}
  \xdef\keep@pages{#1}% Store pages to keep
  \AtBeginShipout{% At shipout, decide whether to discard page/not
    \keeppagefalse%
    \renewcommand*{\do}[1]{% How to handle each page entry in csv list
      \ifnum\value{page}=##1\relax%
        \keeppagetrue% Page should be kept
        \gdef\do####1{}% Do nothing further
      \fi%
    }%
    \expandafter\docsvlist\expandafter{\keep@pages}% Process list of pages to keep
    \ifkeeppage\else\AtBeginShipoutDiscard\fi% Discard page/not
  }%
}
\makeatother
\discardpages{1,3,5,7,9,29,35,40}% Discard these pages.
%\keeppages{2,4,6}% Keep these pages.
\begin{document}
\lipsum[1-50]\lipsum[1-50]\lipsum[1-50]\lipsum[1-50]\lipsum[1-50]
\end{document} 

The above 47-page document is scaled down to 39 after having removed the 8 pages 1, 3, 5, 7, 9, 29, 35, 40 (as supplied by \discardpages{1,3,5,7,9,29,35,40}). To discard zero pages, you could use \discardpages{} (or make no call to \discardpages). Using \keeppages{2,4,6} instead leaves you with a 3-page document consisting of only pages 2, 4 and 6. It should not be that difficult to extend this to include more complicated page ranges.

The above has not been tested with hyperlinks from hyperref.

6
  • 4
    Presumably though for pages that are not shipped out won't write references etc so you'd need to do a full run first then do this with \nofiles to keep the original cross ref info? Feb 1, 2013 at 0:37
  • 1
    Let me add that \nofiles is a command for the preamble, which ensures that auxiliary files are not modified during the current compilation run. Feb 25, 2013 at 8:41
  • 1
    I just tried, and this seems to work with hyperref, though I've tested only links from the TOC. Feb 25, 2013 at 10:18
  • 1
    Instead of comparing the number of the page \ifnum\pdfstrcmp{\thepage}{##1}=\z@% will let you insert the page numbers as they are (would be) on the page. This makes it possible to distinct between normal pages and the frontmatter for example
    – Rubix Cube
    Apr 24, 2016 at 22:42
  • 1
    Is there any way to relabel the page's number once extracted? I have a ToC that appears on every partial document I extracted that way as well as a final page, and then each partial doc is comprised of chapter 1-x of part I and chapter 1-x of part 2. The page numbering correspond to the original and I would like it to be i,ii,iii,... , 1,2,... Jun 22, 2016 at 14:59
48
+25

In the line of David Carlisle's answer, but using pure latex tools, I'm suprised no one mentioned pdfpages package.

You can compile your full document and then extract the required pages using another latex "driving" document whichs uses pdfpages. For example:

\documentclass{article} % Not really important
\usepackage{pdfpages}
\begin{document}
\includepdf[pages={12-15,23,45-49}]{main.pdf}
\end{document}
8
  • 9
    1. This works. 2. This basically disables all links (originally created by hyperref). Feb 25, 2013 at 11:00
  • 1
    Excellent answer! I'll wait though if someone comes up with an answer that will keep all the document-internal links. Feb 25, 2013 at 11:02
  • 2
    The only known solution for this problem is pax (ctan.org/tex-archive/macros/latex/contrib/pax), but it is in experimental status (and I never tried it)
    – JLDiaz
    Feb 25, 2013 at 11:38
  • 1
    Or possibly an adaptation of Werner's answer :-) But you mentioning "Project pax" is a good pointer. Feb 25, 2013 at 15:24
  • 1
    There is also a command-line interface to pdfpages, called pdfjam. The issue I've encountered is that pdfjam has the side-effect of subsetting all fonts, which I don't want. I think it's possible to work around this, however, using something like pdfjam --preamble '\pdfmapfile{=fullembed.map}' (not tested).
    – user6853
    Mar 1, 2013 at 20:28
35
+100

Also the following packages should be mentioned:

  • selectp: [for plain TeX and LaTeX 2.09]

Select pages to be out­put

De­fines a com­mand \out­pu­tonly, whose ar­gu­ment is a list of pages to be out­put. With the com­mand present (be­fore \be­gin{doc­u­ment}), only those pages are out­put.

Select pages to be out­put from a doc­u­ment

Re­quires a file of page num­bers, which spec­i­fies the num­bers of pages to print (in gen­er­ated or­der).

Select pages of a doc­u­ment for out­put

Selects sin­gle pages, ranges of pages, odd pages or even pages for out­put. The pack­age is part of the oberdiek bun­dle.

(http://www.ctan.org/topic/page-control).

4
  • 9
    Something as simple as \usepackage[26-30]{pagesel} seems to do it! Mar 5, 2013 at 18:03
  • 2
    Thanks! And: You would not expect otherwise of a "pack­age [...] of the Oberdiek bun­dle", would you? :-)
    – Stephen
    Mar 7, 2013 at 20:11
  • 4
    This is excellent. Unlike pdfpages, this perserves hyperlinks. Thanks.
    – SigmaX
    Oct 20, 2013 at 5:32
  • 1
    Here is the easiest solution to render from page 1 to page 10. \usepackage[1-10]{pagesel} Feb 5, 2021 at 9:50
20

You can latex the full document and post process with a pdf tool there are several eg this one is on my system

NAME
       pdfseparate - Portable Document Format (PDF) page extractor

SYNOPSIS
       pdfseparate [options] PDF-file PDF-page-pattern

DESCRIPTION
       pdfseparate extract single pages from a Portable Document Format (PDF).

       pdfseparate  reads  the  PDF file PDF-file, extracts one or more pages, and writes one PDF file for
       each page to PDF-page-pattern, PDF-page-pattern should contain %d.  %d is replaced by the page number.

I was going to add that an alternative would be to get tex to only shipout certain pages but @Werner just posted code for such a solution so I'll just leave this as a post processing alternative (which can be more robust depending on the tex code that is interacting with the shipout changes).

6
  • 1
    It so happens I had to do just this a couple of minutes ago, and this is the way I went with. A Google search gave me this full command: pdftk A=inputfile.pdf cat A22-36 output outfile_p22-p36.pdf and it worked as expected.
    – jja
    Mar 1, 2013 at 19:30
  • 2
    @jja: I would not advise anyone to hitch their wagon to pdftk at this point. It uses its own extremely out of date fork of the iText library, which doesn't seem to be receiving any maintenance or bug fixes. When it gets an error from iText, it throws away all the info iText sends back about what the error is. My experience has been that postprocessing pdftex output through tools like gs and pdftk produces many problems with RIPs, and those problems are extremely hard to diagnose.
    – user6853
    Mar 1, 2013 at 20:04
  • 1
    Pdfseparate uses the Poppler library, which seems to be of high quality and well maintained. But it would be a little awkward for this task; if you were going to pull 100 pages out, you'd have to make 100 files using pdfseparate and then concatenate them with pdfunite.
    – user6853
    Mar 2, 2013 at 0:10
  • 1
    @BenCrowell: Are you saying that pdftk is unreliable for this task? I've had some issues with it for other things (adding/extracting metadata, filling out forms, etc.), but for simple concatenation or page extraction it's worked well for me. Mar 5, 2013 at 18:15
  • 2
    @Matthew Leingang: Yes, I'm saying both that pdftk is unreliable for that task and that it's poorly engineered and poorly maintained in general. See bugs.launchpad.net/ubuntu/+source/libitext-java/+bug/1110674
    – user6853
    Mar 6, 2013 at 20:25
4

Here is Werner's solution without the cost of the per-page loops.

\documentclass{article}
\usepackage{lipsum,atbegshi}
\makeatletter
\newcommand{\discardpages}[1]{%
  \xdef\discard@pages{\zap@space#1 \@empty}%
  \AtBeginShipout{%
    \@expandtwoargs\in@{,\the\c@page,}{,\discard@pages,}%
    \ifin@\AtBeginShipoutDiscard\fi
  }%
}
\newcommand{\keeppages}[1]{%
  \xdef\keep@pages{\zap@space#1 \@empty}%
  \AtBeginShipout{%
    \@expandtwoargs\in@{,\the\c@page,}{,\keep@pages,}%
    \ifin@\else\AtBeginShipoutDiscard\fi
  }%
}
\makeatother

% Examples:
\discardpages{1,3,5,7,9,29,35,40}
%\keeppages{2,4,6}
\begin{document}
\lipsum[1-50]\lipsum[1-50]\lipsum[1-50]\lipsum[1-50]\lipsum[1-50]
\end{document} 
1
  • 1
    How to discard only the 1st page? I naively tried with \AtBeginShipout{\ifx\the\c@page1\AtBeginShipoutDiscard\fi} without success. Thanks in advance.
    – mmj
    Jul 9, 2018 at 15:20
4

I can't speak for the OP, but when I do this, I don't really want to use hardcoded page numbers. I want to break up a book into volumes, and \includeonly doesn't do what I want because I want a single table of contents and a single index to be included in both volumes. I also want, e.g., to be able to do a \pageref from vol. 2 back to a page number in vol. 1.

The perl script below is what I use for this purpose. It invokes pdftk to filter the pdf file that was output by pdflatex. Because pdftk is buggy and poorly maintained, I would actually suggest that others do this kind of thing using pdfjam or poppler utils instead.

There is a configuration file that looks like this:

1,2,,3,ch:intro,-1,
1,1,ch:intro,0,splits:startvol2,-3,
2,2,splits:startvol2,-2,splits:index,-1,
1,2,splits:index,0,splits:data,-1,
1,2,splits:data,0,end,,5

The first line, for example, says that both volume 1 and volume 2 should start at page 3 and continue until one page before the first chapter. ch:intro is the latex label for the first chapter.

#!/usr/bin/perl

use strict;

my $input_file = $ARGV[0];
my $vol = $ARGV[1];
my $output_file = $ARGV[2];


# reads save.ref and splits.config
#
# format of splits.config:
#    v1,v2,label1,offset1,label2,offset2,mod8
#    (v1,v2)=(1,1) means only include these pages in volume 1
#    (v1,v2)=(2,2) means only include these pages in volume 2
#    (v1,v2)=(1,2) means include these pages in both volumes
#    label1=latex label of beginning of range, or null
#    offset1=offset from label1; if label1 is null string and offset is +, then take offset1 as page number; if label1 is 'end' and offset is '', take last page
#    label2,offset2=similar for end of range
#    mod8=if not null, force it to start on a page of the output pdf that equals this, modulo 8
#      You typically want the output pdf to have a number of pages that is a multiple of 8. So, e.g., if LM has three pages of data, etc., at the end,
#      set mod8 to 5, so that the third page will equal 7 mod 8.

my %refs = ();
open(F,"<save.ref") or die "error opening save.ref for input";
while (my $line = <F>) {
  chomp $line;
  if ($line =~ /([^,]*),([^,]*),([^,]*)/) {
    my ($label,$name,$page) = ($1,$2,$3);
    $refs{$label} = $page;
  }
}
close(F);

open(F,"<splits.config") or die "error opening splits.config for input";
my @pages = ();
my $n = 0;
while (my $line = <F>) {
  chomp $line;
  if ($line =~ /([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*)/) {
    my ($v1,$v2,$l1,$o1,$l2,$o2,$m) = ($1,$2,$3,$4,$5,$6,$7);
    #print "$v1,$v2,$l1,$o1,$l2,$o2,$m\n";
    my $p1 = find_page($l1,$o1);
    my $p2 = find_page($l2,$o2);
    #print "pages $p1-$p2\n";
    my $chunk = 0;
    if ($vol>=$v1 && $vol<=$v2) {
      if ($p1%2 != 1) {push @pages,"B1"; ++$chunk}
      if ($m ne '') {
        while (($n+$chunk)%8!=$m) {push @pages,"B1"; ++$chunk}
      }
      push @pages,"$p1-$p2";
      if ($p2 ne 'end') {
        $chunk += (($p2-$p1)+1);
        if ($chunk%2!=0) {push @pages,"B1"; ++$chunk}
      }
      print "input $p1-$p2 -> output ",($n+1),"-",($p2 eq 'end' ? 'end' : $n+$chunk),"\n";
      $n += $chunk;
    }
  }
}
close(F);

my $c = "pdftk $input_file B=../share/misc/blank_page.pdf  cat ".join(' ',@pages)." output $output_file";
print "$c\n";
system $c;

sub find_page {
  my $l = shift;
  my $o = shift;
  if ($l eq '') {return $o}
  if ($l eq 'end') {return 'end'}
  if (!exists $refs{$l}) {die "label $l doesn't exist in save.ref"}
  return $refs{$l}+$o;
}

sub barf {
  my $message = shift;
  print STDERR "splits.pl: $message\n";
  print STDERR "You will need to edit splits.config.\n";
  exit(-1);
}
1

@Lover of Structure's comment as an answer:

\usepackage[26-30]{pagesel}

Include this before begin{document}. No other commands or extra files needed.

If you get a "temporary page" after the final page that won't go away, add a few \newpage and \clearpage commands followed by some random text just before \end{document} to add some extra pages, which Latex can then for some reason figure out how to trim off.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .