紙は重いので,資料はスキャンしてPDFにしてタブレットで見たい!
でも資料が 小冊子印刷 されているとスキャンしてもページの順番が滅茶苦茶になってしまうので,どうにかしたい!
小冊子印刷とは
説明が面倒なので, Adobe Acrobatのマニュアルから引用すると以下の通り.
小冊子とは、複数のページからなる文書です。各ページは用紙を 2 つ折りにしたときに順序が正しくなるように配列されます。 両面印刷された用紙をページ順に重ね、2 つ折にして綴じると、ページが正しく配列された 1 つの冊子ができあがります。
※ 総ページ数、右綴じ、左綴じの設定により、配置されるページは異なります。
ページ数の対応
-
$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
#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
これで紙とおさらばできる,やったね!