Last active
September 12, 2025 01:46
-
-
Save kujirahand/5fa0f3618ed53dcd91eb5fe45f2e5e1c to your computer and use it in GitHub Desktop.
PDF分割プログラムです。このプログラムを元にして作った配布用の実行ファイルがあります。コメント欄をご覧ください。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| use lopdf::{dictionary, Document, Object, ObjectId}; | |
| use std::collections::BTreeMap; | |
| use std::env; | |
| fn main() { | |
| let args: Vec<String> = env::args().collect(); | |
| if args.len() < 2 { | |
| println!("使い方: pdf_splitter <file.pdf>"); | |
| return; | |
| } | |
| let input_path = &args[1]; | |
| if let Err(e) = split_pdf(input_path) { | |
| eprintln!("エラー: {}", e); | |
| } | |
| } | |
| fn split_pdf(input_path: &str) -> Result<(), Box<dyn std::error::Error>> { | |
| let doc = Document::load(input_path)?; | |
| let pages = doc.get_pages(); | |
| let page_count = pages.len(); | |
| let base_name = input_path.trim_end_matches(".pdf"); | |
| for i in (0..page_count).step_by(10) { | |
| let mut new_doc = Document::with_version(doc.version.clone()); | |
| let mut copied_objects: BTreeMap<ObjectId, ObjectId> = BTreeMap::new(); | |
| let mut new_page_ids = vec![]; | |
| let start_page = i + 1; | |
| let end_page = (i + 10).min(page_count); | |
| for page_num in start_page..=end_page { | |
| if let Some(&page_id) = pages.get(&(page_num as u32)) { | |
| copy_object_and_dependencies(page_id, &doc, &mut new_doc, &mut copied_objects)?; | |
| new_page_ids.push(copied_objects.get(&page_id).cloned().unwrap()); | |
| } | |
| } | |
| if new_page_ids.is_empty() { | |
| continue; | |
| } | |
| let pages_id = new_doc.add_object(Object::Dictionary(dictionary! { | |
| "Type" => "Pages", | |
| "Kids" => new_page_ids.iter().map(|id| Object::Reference(*id)).collect::<Vec<_>>(), | |
| "Count" => new_page_ids.len() as i64, | |
| })); | |
| let catalog_id = new_doc.add_object(Object::Dictionary(dictionary! { | |
| "Type" => "Catalog", | |
| "Pages" => Object::Reference(pages_id), | |
| })); | |
| new_doc.trailer.set("Root", Object::Reference(catalog_id)); | |
| let output_path = format!("{}_{}.pdf", base_name, i / 10 + 1); | |
| new_doc.save(&output_path)?; | |
| println!("{} を保存しました。", output_path); | |
| } | |
| Ok(()) | |
| } | |
| fn copy_object_and_dependencies( | |
| object_id: ObjectId, | |
| old_doc: &Document, | |
| new_doc: &mut Document, | |
| copied_objects: &mut BTreeMap<ObjectId, ObjectId>, | |
| ) -> Result<ObjectId, Box<dyn std::error::Error>> { | |
| if let Some(new_id) = copied_objects.get(&object_id) { | |
| return Ok(*new_id); | |
| } | |
| // Crucially, insert a placeholder ID before recursing to break cycles. | |
| let new_id = new_doc.new_object_id(); | |
| copied_objects.insert(object_id, new_id); | |
| let obj = old_doc.get_object(object_id)?.clone(); | |
| let new_obj = deep_copy_object(obj, old_doc, new_doc, copied_objects)?; | |
| // Now, insert the fully processed object at the placeholder ID. | |
| new_doc.objects.insert(new_id, new_obj); | |
| Ok(new_id) | |
| } | |
| fn deep_copy_object( | |
| obj: Object, | |
| old_doc: &Document, | |
| new_doc: &mut Document, | |
| copied_objects: &mut BTreeMap<ObjectId, ObjectId>, | |
| ) -> Result<Object, Box<dyn std::error::Error>> { | |
| let new_obj = match obj { | |
| Object::Reference(id) => { | |
| let new_id = copy_object_and_dependencies(id, old_doc, new_doc, copied_objects)?; | |
| Object::Reference(new_id) | |
| } | |
| Object::Array(arr) => { | |
| let mut new_arr = vec![]; | |
| for item in arr { | |
| new_arr.push(deep_copy_object(item, old_doc, new_doc, copied_objects)?); | |
| } | |
| Object::Array(new_arr) | |
| } | |
| Object::Dictionary(mut dict) => { | |
| for (_, val) in dict.iter_mut() { | |
| *val = deep_copy_object(val.clone(), old_doc, new_doc, copied_objects)?; | |
| } | |
| Object::Dictionary(dict) | |
| } | |
| Object::Stream(mut stream) => { | |
| for (_, val) in stream.dict.iter_mut() { | |
| *val = deep_copy_object(val.clone(), old_doc, new_doc, copied_objects)?; | |
| } | |
| Object::Stream(stream) | |
| } | |
| _ => obj.clone(), | |
| }; | |
| Ok(new_obj) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
バンドルのための追加のファイルの説明
macOSのアプリケーションバンドル:
% tree pdf_splitter.app pdf_splitter.app └── Contents ├── Info.plist └── MacOS ├── pdf_splitter └── pdf_splitter_binmacOS用のInfo.plistファイル: