Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

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.
share|improve this question
1  
So \input, \includeonly type of solutions are not allowed right? I mean like this tex.stackexchange.com/questions/3462/… –  percusse Feb 1 '13 at 0:28
    
@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! –  Lover of Structure Feb 1 '13 at 14:28

6 Answers 6

up vote 24 down vote accepted

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.

share|improve this answer
3  
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? –  David Carlisle Feb 1 '13 at 0:37
    
Let me add that \nofiles is a command for the preamble, which ensures that auxiliary files are not modified during the current compilation run. –  Lover of Structure Feb 25 '13 at 8:41
    
I just tried, and this seems to work with hyperref, though I've tested only links from the TOC. –  Lover of Structure Feb 25 '13 at 10:18

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).

share|improve this answer
    
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 '13 at 19:30
    
@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. –  Ben Crowell Mar 1 '13 at 20:04
    
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. –  Ben Crowell Mar 2 '13 at 0:10
    
@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. –  Matthew Leingang Mar 5 '13 at 18:15
    
@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 –  Ben Crowell Mar 6 '13 at 20: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}
share|improve this answer
2  
1. This works. 2. This basically disables all links (originally created by hyperref). –  Lover of Structure Feb 25 '13 at 11:00
    
Excellent answer! I'll wait though if someone comes up with an answer that will keep all the document-internal links. –  Lover of Structure Feb 25 '13 at 11:02
1  
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 '13 at 11:38
    
Or possibly an adaptation of Werner's answer :-) But you mentioning "Project pax" is a good pointer. –  Lover of Structure Feb 25 '13 at 15:24
    
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). –  Ben Crowell Mar 1 '13 at 20:28

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).

share|improve this answer
1  
Something as simple as \usepackage[26-30]{pagesel} seems to do it! –  Lover of Structure Mar 5 '13 at 18:03
1  
Thanks! And: You would not expect otherwise of a "pack­age [...] of the Oberdiek bun­dle", would you? :-) –  Stephen Mar 7 '13 at 20:11
    
This is excellent. Unlike pdfpages, this perserves hyperlinks. Thanks. –  SigmaX Oct 20 '13 at 5:32

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);
}
share|improve this answer

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} 
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.