小冊子印刷されたPDFから元のPDFを復元する


紙は重いので,資料はスキャンしてPDFにしてタブレットで見たい!
でも資料が 小冊子印刷 されているとスキャンしてもページの順番が滅茶苦茶になってしまうので,どうにかしたい!

小冊子印刷とは

説明が面倒なので, Adobe Acrobatのマニュアルから引用すると以下の通り.

小冊子とは、複数のページからなる文書です。各ページは用紙を 2 つ折りにしたときに順序が正しくなるように配列されます。 両面印刷された用紙をページ順に重ね、2 つ折にして綴じると、ページが正しく配列された 1 つの冊子ができあがります。
※ 総ページ数、右綴じ、左綴じの設定により、配置されるページは異なります。


https://helpx.adobe.com/jp/acrobat/kb/6264.html より引用)

ページ数の対応

  • $2N$ ページ1 の文書を小冊子印刷することを考えます.
  • 元のPDFで $i$ ページ目にあったページが,小冊子印刷後には $j$ ページ目に移るとします.ここで,小冊子印刷後のページ番号は下図のように数えます.

  • ちょっと考えると,
    $$j(i) =
    \begin{cases}
    2i - i {\small \%}2 & (\text{if}\ 1 \leq i\leq N) \\
    2N -2(i-N-1) - (i-N){\small \%}2 & (\text{if}\ N+1 \leq i \leq 2N)
    \end{cases}
    $$
    であることがわかります.

実装

下図の要領でやります.

1.左右に分割

これはAcrobatとかでただポスター印刷すればよいでしょう.

上の画像では分割ついでにページサイズを一つ上げて($1.41 \simeq \sqrt{2}$倍して)います.

2."j(1)⊔j(2)⊔j(3)…j(2N−1)⊔j(2N)"を出力

今回は標準入力から$N$(左右分割前のページ数)を受け取り標準出力に渡しました.c++で雑に書くとこんな感じ.2

booklet.cpp
#include <bits/stdc++.h>
using namespace std;

map<int,int> booklet(int N) {
    map<int,int> j; // 元の文書でのiページは小冊子印刷された文書でのjページに
    for(int i=1;i<=N;i++) j[i] = 2*i - i%2;
    for(int i=N+1;i<=2*N;i++) j[i] = 2*N - 2*(i-N-1) - (i-N)%2;
    return j;
}

int main() {
    int N; // 小冊子印刷後(左右分割前)のページ数
    cout << "How many pages?" << endl;
    cin >> N;
    auto j = booklet(N);
    cout << "ans:" << endl;
    for(int i=1;i<=2*N;i++){
        cout << j[i];
        if(i<2*N) cout << " ";
        else cout << endl;
    }
    return 0;
}

3.並び替え

今回はページの並び替えにpdftkを用いました.
https://www.pdflabs.com/tools/pdftk-the-pdf-toolkit/ からインストーラをダウンロードしインストール.準備はこれで完了.

例えばコマンドラインに

> pdftk pdf_original.pdf cat 1 3 2 4-end output output.pdf

と入力すると元のPDF(pdf_original.pdf)の2ページ目と3ページ目を入れ替えたPDF(output.pdf)が生成されます.3

同様に,
$$
\text{hoge} = \text{“}j(1) \sqcup j(2) \sqcup j(3) \ldots j(2N-1) \sqcup j(2N)\text{”}
$$
として,

> pdftk pdf_original.pdf cat hoge output output.pdf

とすれば良いですね.

やってみた

     ↓ 左右分割

booklet.cpp をコンパイルして実行:

> g++ booklet.cpp
> ./a.exe
How many pages?
4
ans:
1 4 5 8 7 6 3 2

"$j(1) \sqcup j(2) \sqcup j(3) \ldots j(7) \sqcup j(8)$" が得られたので代入! pdftk で並び替え:

> pdftk booklet_divided.pdf cat 1 4 5 8 7 6 3 2 output booklet_output.pdf

これで紙とおさらばできる,やったね!


  1. ページ数が奇数の場合,小冊子印刷の際に空白ページが挿入されるので,ページ数が偶数の場合に帰着されます. 

  2. 逆引きの可能性を加味してmapにしましたが,必要なかった感が否めません. 

  3. 参考:https://jidouka.work/?p=304