pdf-slide-divider スライドが1ページに複数面割り付けられている(例えば3×3面とか2×2面とか)PDFファイルを1ページ1面ごとに分割する
2020/11/29 3:09
このままだと製本やファイリングしにくいので、1面1枚ずつに分割したいときがあります。
そのときのためのツールです。
「2 in 1 印刷」や「冊子印刷」に対応した形式でも出力できます。
※冊子印刷:週刊誌形式。用紙を重ねてまとめて半分に折って、折った線上をホチキス(ステープラ)でとめたときに、冊子状になる形式。
処理のイメージ

Perlで書いています。Imagemagick が要ります。一度画像にしてから再度 PDF に戻しているので、ちょっと動作が重たいです・・・。
ウェブ上で使える、CGI版も用意しました。
https://satomichan.jp/cgi/pdf-slide-divider/
ソースは
#!/usr/bin/perl -w use strict; use utf8; use File::Basename; our $magick_convert_cmd = '/usr/bin/convert'; our $magick_mogrify_cmd = '/usr/bin/mogrify'; our $identify_cmd = '/usr/bin/identify'; our $rm_cmd = '/bin/rm'; our $temp_dir = (-d './temp' ? './temp' : '.'); #ARGV[0] オリジナルPDFファイル名 #ARGV[1] 分割後のPDFファイル名 #ARGV[2] オリジナル1ページあたりの、ヨコに割り付けられているページ数 #ARGV[3] オリジナル1ページあたりの、タテに割り付けられているページ数 #ARGV[4] オリジナルPDFファイル 余白率(%) #ARGV[5] 生成後ページから余白除去するか (1=する/0=しない) #ARGV[6] 2in1 出力するか (1=する/0=しない;1枚ごと) #ARGV[7] 冊子出力するか (1=する/0=しない) #ARGV[8] A4縦にリサイズするか (1=する/0=しない) #ARGV[9] density 解像度 (Default=400) # ex) ./pdfdivider.pl in.pdf out.pdf 3 3 6 1 1 1 1 200 my($org_pdf, $divided_pdf, $x_pages, $y_pages, $space, $is_trim, $is_2in1, $is_booklet, $is_resize_a4v, $density) = @ARGV; die unless ($org_pdf && $divided_pdf); #一時ファイル用ディレクトリを変更 my $new_temp_dir = "${temp_dir}/". basename($org_pdf); mkdir($new_temp_dir) or die; $temp_dir = $new_temp_dir; #オリジナルPDFのページ数取得 my $org_pages; open(my $PAGES, "$identify_cmd $org_pdf | /usr/bin/wc -l |"); my $line = <$PAGES>; if($line =~ /(\d+)/){ $org_pages = $1; }else{ die "Can't get number of pages."; } close($PAGES); # my $x_percent = 100 / $x_pages; my $y_percent = 100 / $y_pages; my @splitted_pages; foreach (my $page = 0; $page < $org_pages; $page++){ my $temp_basefn_a_page = "temp_". basename($org_pdf). "_${page}"; system("$magick_convert_cmd -density $density +profile icc ${org_pdf}[${page}] ${temp_dir}/${temp_basefn_a_page}.bmp"); my($x, $y) = get_image_size("${temp_dir}/${temp_basefn_a_page}.bmp"); my $x_center = $x - int( $x * $space / 100 ); my $y_center = $y - int( $y * $space / 100 ); system("$magick_mogrify_cmd -gravity center -crop ${x_center}x${y_center}+0+0 ${temp_dir}/${temp_basefn_a_page}.bmp"); system("$magick_convert_cmd ${temp_dir}/${temp_basefn_a_page}.bmp +profile icc -crop ${x_percent}%x${y_percent}% ${temp_dir}/${temp_basefn_a_page}.bmp"); for my $p (0..($x_pages * $y_pages - 1) ){ #生成後ページから余白除去 if($is_trim){ system("$magick_mogrify_cmd -trim -fuzz 1% +repage ${temp_dir}/${temp_basefn_a_page}-${p}.bmp"); } my($x_trimed, $y_trimed) = &get_image_size("${temp_dir}/${temp_basefn_a_page}-${p}.bmp"); push(@splitted_pages, "${temp_basefn_a_page}-${p}.bmp") unless ($x_trimed == 1 && $y_trimed == 1); } } #2枚ずつ縦に結合 if($is_2in1){ my @joined_pages; while(my $up = shift @splitted_pages){ my $down = shift @splitted_pages; if(!defined $down){ my($up_size_x, $up_size_y) = &get_image_size("${temp_dir}/$up"); $down = basename($org_pdf). "_whitepage.bmp"; system("$magick_convert_cmd -size ${up_size_x}x${up_size_y} xc:white ${temp_dir}/$down"); } my $joined = "temp_joined_". $up. "_and_". $down; system("$magick_convert_cmd -append ${temp_dir}/$up ${temp_dir}/$down ${temp_dir}/$joined"); push(@joined_pages, $joined); } @splitted_pages = @joined_pages; } #冊子出力 if($is_booklet){ @splitted_pages = &make_booklet(@splitted_pages); } #強制A4縦リサイズ if($is_resize_a4v){ resize_a4v($density, @splitted_pages); } my @splitted_pages2; foreach my $a_p (@splitted_pages){ push(@splitted_pages2, "${temp_dir}/$a_p"); } system("$magick_convert_cmd -density $density -units PixelsPerInch -compress zip ". join(" ", @splitted_pages2). " $divided_pdf"); system("$rm_cmd ${temp_dir}/*"); system("$rm_cmd -r ${temp_dir}"); exit; sub make_booklet { my(@org_pages) = @_; my @booklet_pages; #4の倍数になっていないとき my $r = ($#org_pages + 1) % 4; foreach my $i (1.. 4 - $r){ my $t_page = $org_pages[$#org_pages -$i +1]; my($t_x, $t_y) = &get_image_size("${temp_dir}/${t_page}"); my $white_page = "booklet_white_${i}.bmp"; system("$magick_convert_cmd -size ${t_x}x${t_y} xc:white ${temp_dir}/${white_page}"); push(@org_pages, $white_page); } while(1){ #A面 push(@booklet_pages, &make_booklet_half( pop(@org_pages) , shift(@org_pages) )); #B面 push(@booklet_pages, &make_booklet_half( shift(@org_pages) , pop(@org_pages) )); last if(@org_pages == 0); } return @booklet_pages; } sub make_booklet_half { my($left, $right) = @_; my($l_x, $l_y) = &get_image_size("${temp_dir}/$left"); my($r_x, $r_y) = &get_image_size("${temp_dir}/$right"); #左右の幅xが違ったら、そろえる my $diff = 0; if($l_x > $r_x){ #左が幅広→右に空白を付加 $diff = $l_x - $r_x; system("$magick_convert_cmd -size ${diff}x5 xc:white ${temp_dir}/diff_white.bmp"); system("$magick_convert_cmd ${temp_dir}/diff_white.bmp ${temp_dir}/$right +append ${temp_dir}/appended.bmp"); rename("${temp_dir}/appended.bmp", "${temp_dir}/$right"); }elsif($l_x < $r_x){ #右が幅広→左に空白を付加 $diff = $r_x - $l_x; system("$magick_convert_cmd -size ${diff}x5 xc:white ${temp_dir}/diff_white.bmp"); system("$magick_convert_cmd ${temp_dir}/$left ${temp_dir}/diff_white.bmp +append ${temp_dir}/appended.bmp"); rename("${temp_dir}/appended.bmp", "${temp_dir}/$left"); } #パンチ穴よけマージン生成 my $m = int( ( 0.13 * ($l_x + $r_x + $diff) ) / (1-0.13) ); #my $m = $density * 1.5; system("$magick_convert_cmd -size ${m}x5 xc:white ${temp_dir}/center_white.bmp"); #連結 my $ret = "${left}_${right}_joined.bmp"; system("$magick_convert_cmd ${temp_dir}/$left ${temp_dir}/center_white.bmp ${temp_dir}/$right +append ${temp_dir}/${ret}"); return $ret; } sub get_image_size { my($image_filename) = @_; my $x_size = -1; my $y_size = -1; open(my $INFO, "$identify_cmd $image_filename |"); my $line = <$INFO>; if($line =~ / (\d+)x(\d+) /){ $x_size = $1; $y_size = $2; } close($INFO); return ($x_size, $y_size); } sub resize_a4v { my($density, @pages) = @_; my $a4x = int( $density * 8.27 + 0.5); my $a4y = int( $density * 11.69 + 0.5); foreach my $a_page (@pages){ my($p_x, $p_y) = &get_image_size("${temp_dir}/${a_page}"); if($p_x > $p_y){ system("$magick_mogrify_cmd -rotate -90 ${temp_dir}/${a_page}"); } system("$magick_mogrify_cmd -resize ${a4x}x${a4y} ${temp_dir}/${a_page}"); my($resized_x, $resized_y) = &get_image_size("${temp_dir}/${a_page}"); if($a4x > $resized_x){ my $diff_x = $a4x - $resized_x; my $diff_l = int( $diff_x /2); my $diff_r = $diff_x - $diff_l; system("$magick_convert_cmd -size ${diff_l}x${a4y} xc:white ${temp_dir}/diff_white_l.bmp"); system("$magick_convert_cmd -size ${diff_r}x${a4y} xc:white ${temp_dir}/diff_white_r.bmp"); system("$magick_convert_cmd ${temp_dir}/diff_white_l.bmp ${temp_dir}/${a_page}". " ${temp_dir}/diff_white_r.bmp +append ${temp_dir}/appended_a4v.bmp"); rename("${temp_dir}/appended_a4v.bmp", "${temp_dir}/${a_page}"); }elsif($a4y > $resized_y){ my $diff_y = $a4y - $resized_y; my $diff_t = int( $diff_y /2); my $diff_b = $diff_y - $diff_t; system("$magick_convert_cmd -size ${a4x}x${diff_t} xc:white ${temp_dir}/diff_white_t.bmp"); system("$magick_convert_cmd -size ${a4x}x${diff_b} xc:white ${temp_dir}/diff_white_b.bmp"); system("$magick_convert_cmd ${temp_dir}/diff_white_t.bmp ${temp_dir}/${a_page}". " ${temp_dir}/diff_white_b.bmp -append ${temp_dir}/appended_a4v.bmp"); rename("${temp_dir}/appended_a4v.bmp", "${temp_dir}/${a_page}"); } } }