LoginSignup
2
2

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-12-31

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

小冊子印刷とは

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

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


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

ページ数の対応

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

  • ちょっと考えると,
    $$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}
    $$
    であることがわかります.

実装

下図の要領でやります.
booklet_02.png

1.左右に分割

これはAcrobatとかでただポスター印刷すればよいでしょう.
divide.PNG
上の画像では分割ついでにページサイズを一つ上げて($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 

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2