Open8

nb2pdf

kuro-140kuro-140

コマンドライン引数からファイル名の読み取り方法

main.rs

use std::env;
use std::path::Path;

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        eprintln!("Usage: {} <file_name>", args[0]);
        return;
    }
    let file_name = &args[1];
    let file_path = Path::new(file_name);
    if file_path.exists() && file_path.is_file() {
        println!("File {} already exists!", file_name);
    } else {
        println!("File {} does not exist!", file_name);
        return;
    }

    println!("file_name is {}!", file_name);
}
kuro-140kuro-140

printpdfのadd on Windows11

実行コマンドと結果
> cargo add printpdf
    Updating crates.io index
      Adding printpdf v0.8.2 to dependencies
             Features:
             + azul-core
             + azul-css
             + azul-layout
             + html
             + kuchiki
             + rust-fontconfig
             + text_layout
             + xmlparser
             - bmp
             - dds
             - gif
             - hdr
             - ico
             - jpeg
             - js-sys
             - png
             - pnm
             - rayon
             - tga
             - tiff
             - wasm-bindgen-futures
             - web-sys
             - webp
    Updating crates.io index
    Blocking waiting for file lock on package cache
     Locking 231 packages to latest compatible versions
      Adding adler2 v2.0.0
      Adding aes v0.8.4
      Adding aliasable v0.1.3
      Adding alloc-no-stdlib v2.0.4
      Adding alloc-stdlib v0.2.2
      Adding allsorts-subset-browser v0.16.0
      Adding arrayref v0.3.9
      Adding arrayvec v0.7.6
      Adding atty v0.2.14
      Adding autocfg v1.4.0
      Adding azul-core v0.0.5
      Adding azul-css v0.0.5
      Adding azul-layout v0.0.5
      Adding azul-simplecss v0.1.1
      Adding base64 v0.22.1
      Adding bitflags v1.3.2
      Adding bitflags v2.9.0
      Adding bitreader v0.3.11
      Adding block-buffer v0.10.4
      Adding block-padding v0.3.3
      Adding brotli-decompressor v4.0.3
      Adding bumpalo v3.17.0
      Adding bytecount v0.6.8
      Adding bytemuck v1.23.0
      Adding byteorder v1.5.0
      Adding byteorder-lite v0.1.0
      Adding cbc v0.1.2
      Adding cbindgen v0.24.5
      Adding cfg-if v1.0.0
      Adding cipher v0.4.4
      Adding clap v3.2.25
      Adding clap_lex v0.2.4
      Adding color_quant v1.1.0
      Adding convert_case v0.4.0
      Adding core_maths v0.1.1
      Adding cpufeatures v0.2.17
      Adding crc32fast v1.4.2
      Adding crypto-common v0.1.6
      Adding cssparser v0.27.2
      Adding cssparser-macros v0.6.1
      Adding data-url v0.3.1
      Adding deranged v0.4.0
      Adding derive_more v0.99.20
      Adding digest v0.10.7
      Adding dtoa v1.0.10
      Adding dtoa-short v0.3.5
      Adding either v1.15.0
      Adding encoding_rs v0.8.35
      Adding equivalent v1.0.2
      Adding errno v0.3.11
      Adding fastrand v2.3.0
      Adding fdeflate v0.3.7
      Adding flate2 v1.1.1
      Adding float-cmp v0.9.0
      Adding fontconfig-parser v0.5.8
      Adding fontdb v0.23.0
      Adding futf v0.1.5
      Adding fxhash v0.2.1
      Adding generic-array v0.14.7
      Adding getrandom v0.1.16
      Adding getrandom v0.3.3
      Adding gif v0.13.1
      Adding gl-context-loader v0.1.8
      Adding glyph-names v0.2.0
      Adding hashbrown v0.12.3
      Adding hashbrown v0.15.3
      Adding heck v0.4.1
      Adding hermit-abi v0.1.19
      Adding highway v0.8.1
      Adding html5ever v0.25.2
      Adding image v0.25.6
      Adding image-webp v0.2.1
      Adding imagesize v0.13.0
      Adding indexmap v1.9.3
      Adding indexmap v2.9.0
      Adding inout v0.1.4
      Adding itertools v0.10.5
      Adding itoa v0.4.8
      Adding js-sys v0.3.77
      Adding kuchiki v0.8.1
      Adding kurbo v0.11.2
      Adding lazy_static v1.5.0
      Adding libc v0.2.172
      Adding libm v0.2.15
      Adding linux-raw-sys v0.9.4
      Adding lock_api v0.4.12
      Adding log v0.4.27
      Adding lopdf v0.35.0
      Adding mac v0.1.1
      Adding markup5ever v0.10.1
      Adding matches v0.1.10
      Adding md-5 v0.10.6
      Adding memmap2 v0.9.5
      Adding minimal-lexical v0.2.1
      Adding miniz_oxide v0.8.8
      Adding mmapio v0.9.1
      Adding new_debug_unreachable v1.0.6
      Adding nodrop v0.1.14
      Adding nom v7.1.3
      Adding nom_locate v4.2.0
      Adding num-conv v0.1.0
      Adding num-traits v0.2.19
      Adding once_cell v1.21.3
      Adding os_str_bytes v6.6.1
      Adding ouroboros v0.17.2
      Adding ouroboros_macro v0.17.2
      Adding parking_lot v0.12.3
      Adding parking_lot_core v0.9.10
      Adding pathfinder_geometry v0.5.1
      Adding pathfinder_simd v0.5.4
      Adding pdf-writer v0.12.1
      Adding phf v0.8.0
      Adding phf_codegen v0.8.0
      Adding phf_generator v0.8.0
      Adding phf_generator v0.11.3
      Adding phf_macros v0.8.0
      Adding phf_shared v0.8.0
      Adding phf_shared v0.11.3
      Adding pico-args v0.5.0
      Adding png v0.17.16
      Adding powerfmt v0.2.0
      Adding ppv-lite86 v0.2.21
      Adding precomputed-hash v0.1.1
      Adding printpdf v0.8.2
      Adding proc-macro-error v1.0.4
      Adding proc-macro-error-attr v1.0.4
      Adding proc-macro-hack v0.5.20+deprecated
      Adding quick-error v2.0.1
      Adding r-efi v5.2.0
      Adding rand v0.7.3
      Adding rand v0.8.5
      Adding rand_chacha v0.2.2
      Adding rand_core v0.5.1
      Adding rand_core v0.6.4
      Adding rand_hc v0.2.0
      Adding rand_pcg v0.2.1
      Adding rangemap v1.5.1
      Adding redox_syscall v0.5.12
      Adding resvg v0.45.1
      Adding rgb v0.8.50
      Adding roxmltree v0.14.1
      Adding roxmltree v0.20.0
      Adding rust-fontconfig v1.0.1
      Adding rustc-hash v1.1.0
      Adding rustc_version v0.4.1
      Adding rustix v1.0.7
      Adding rustversion v1.0.20
      Adding rustybuzz v0.20.1
      Adding scopeguard v1.2.0
      Adding selectors v0.22.0
      Adding semver v1.0.26
      Adding servo_arc v0.1.1
      Adding simd-adler32 v0.3.7
      Adding simplecss v0.2.2
      Adding siphasher v0.3.11
      Adding siphasher v1.0.1
      Adding slotmap v1.0.7
      Adding smallvec v1.15.0
      Adding stable_deref_trait v1.2.0
      Adding static_assertions v1.1.0
      Adding strict-num v0.1.1
      Adding string_cache v0.8.9
      Adding string_cache_codegen v0.5.4
      Adding strsim v0.10.0
      Adding subsetter v0.2.1
      Adding svg2pdf v0.13.0
      Adding svgtypes v0.15.3
      Adding syn v1.0.109
      Adding tempfile v3.20.0
      Adding tendril v0.4.3
      Adding termcolor v1.4.1
      Adding textwrap v0.16.2
      Adding thin-slice v0.1.1
      Adding thiserror v2.0.12
      Adding thiserror-impl v2.0.12
      Adding time v0.3.41
      Adding time-core v0.1.4
      Adding time-macros v0.2.22
      Adding tiny-skia v0.11.4
      Adding tiny-skia-path v0.11.4
      Adding tinyvec v1.9.0
      Adding tinyvec_macros v0.1.1
      Adding toml v0.5.11
      Adding ttf-parser v0.15.2
      Adding ttf-parser v0.25.1
      Adding typenum v1.18.0
      Adding ucd-trie v0.1.7
      Adding unicode-bidi v0.3.18
      Adding unicode-bidi-mirroring v0.4.0
      Adding unicode-canonical-combining-class v0.5.0
      Adding unicode-ccc v0.4.0
      Adding unicode-general-category v0.6.0
      Adding unicode-joining-type v0.7.0
      Adding unicode-normalization v0.1.24
      Adding unicode-properties v0.1.3
      Adding unicode-script v0.5.7
      Adding unicode-vo v0.1.0
      Adding usvg v0.45.1
      Adding utf-8 v0.7.6
      Adding version_check v0.9.5
      Adding wasi v0.9.0+wasi-snapshot-preview1
      Adding wasi v0.14.2+wasi-0.2.4
      Adding wasm-bindgen v0.2.100
      Adding wasm-bindgen-backend v0.2.100
      Adding wasm-bindgen-futures v0.4.50
      Adding wasm-bindgen-macro v0.2.100
      Adding wasm-bindgen-macro-support v0.2.100
      Adding wasm-bindgen-shared v0.2.100
      Adding web-sys v0.3.77
      Adding weezl v0.1.9
      Adding winapi v0.3.9
      Adding winapi-i686-pc-windows-gnu v0.4.0
      Adding winapi-util v0.1.9
      Adding winapi-x86_64-pc-windows-gnu v0.4.0
      Adding windows-sys v0.59.0
      Adding windows-targets v0.52.6
      Adding windows_aarch64_gnullvm v0.52.6
      Adding windows_aarch64_msvc v0.52.6
      Adding windows_i686_gnu v0.52.6
      Adding windows_i686_gnullvm v0.52.6
      Adding windows_i686_msvc v0.52.6
      Adding windows_x86_64_gnu v0.52.6
      Adding windows_x86_64_gnullvm v0.52.6
      Adding windows_x86_64_msvc v0.52.6
      Adding wit-bindgen-rt v0.39.0
      Adding xmlparser v0.13.6
      Adding xmlwriter v0.1.0
      Adding zerocopy v0.8.25
      Adding zerocopy-derive v0.8.25
      Adding zune-core v0.4.12
      Adding zune-jpeg v0.4.14
kuro-140kuro-140

https://qiita.com/tagawa0525/items/7b41bd34b8efa9d8d9e1
この記事では、printpdfを使ってpdfファイルを生成するサンプルコードが載っていた。
リンク切れ対策のため、pdfファイル生成のコードを引用する。

引用した動作確認済みソースコード (src/main.rs)
use printpdf::*;
use std::fs::File;
use std::io::{BufReader, Read};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // PDFドキュメントを作成
    let mut doc = PdfDocument::new("from text file");

    // フォントをシステムから読み込む
    let font_bytes = std::fs::read("C:/Windows/Fonts/UDDigiKyokashoN-R.ttc")?;

    // 警告メッセージの格納場所を作成
    let font_index = 0;
    let mut warnings = Vec::new();

    // フォントをロード - ParsedFontを使って処理します(追加の引数が必要)
    let font = match ParsedFont::from_bytes(&font_bytes, font_index, &mut warnings) {
        Some(font) => font,
        None => {
            let msg = format!("フォントの読み込みに失敗しました : {:?}", warnings);
            eprintln!("{}", msg);
            return Err(msg.into());
        }
    };

    // フォントをドキュメントに追加
    let font_id = doc.add_font(&font);

    // テキストファイルを読み込む
    let input_file = File::open("src/main.rs")?;
    let mut reader = BufReader::new(input_file);
    let mut text = String::new();
    reader.read_to_string(&mut text)?;

    // 複数のページを格納するベクター
    let mut pages = Vec::new();

    // テキスト行のイテレータを作成
    let lines = text.lines().collect::<Vec<_>>();
    let mut line_index = 0;

    // ページを作成してテキストを追加する
    while line_index < lines.len() {
        // 新しいページのコンテンツを準備
        let mut page_contents = vec![
            Op::SetLineHeight { lh: Pt(14.0) },          // 適切な行間に調整
            Op::SetWordSpacing { pt: Pt(0.0) },          // 単語間隔を標準に
            Op::SetCharacterSpacing { multiplier: 0.0 }, // 文字間隔を標準に
        ];

        // フォントを設定
        page_contents.push(Op::SetFontSize {
            font: font_id.clone(),
            size: Pt(12.0),
        });

        // 各ページの開始Y位置
        let mut y_pos = Mm(280.0); // A4の上部から開始(余白を考慮)

        // このページに表示する行を追加
        while line_index < lines.len() {
            let line = lines[line_index];

            // テキストセクション開始
            page_contents.push(Op::StartTextSection);

            // テキスト位置を設定
            let text_pos = Point {
                x: Mm(10.0).into(),
                y: y_pos.into(),
            };
            page_contents.push(Op::SetTextCursor { pos: text_pos });

            // テキストを追加
            page_contents.push(Op::WriteText {
                items: vec![TextItem::Text(line.to_string())],
                font: font_id.clone(),
            });

            // テキストセクション終了
            page_contents.push(Op::EndTextSection);

            // Y位置を更新
            y_pos -= Mm(6.0); // 行間を適切に調整

            // 次の行に進む
            line_index += 1;

            // ページ下限に達したら、このページを終了
            if y_pos < Mm(20.0) {
                break;
            }
        }

        // ページを作成して追加
        let page = PdfPage::new(Mm(210.0), Mm(297.0), page_contents); // A4サイズ
        pages.push(page);

        println!("ページ {} 完成", pages.len());
    }

    // 保存オプションを設定
    let save_options = PdfSaveOptions {
        subset_fonts: false,
        // optimize: false, // デバッグのために最適化を無効化
        ..Default::default()
    };

    // PDFを生成して保存
    let mut warnings = Vec::new();
    let page_length = pages.len();
    let pdf_bytes = doc.with_pages(pages).save(&save_options, &mut warnings);
    // 警告メッセージがあれば表示
    if !warnings.is_empty() {
        println!("警告メッセージ: {:?}", warnings);
    }

    // ファイルに書き込み
    match std::fs::write("output.pdf", pdf_bytes) {
        Ok(_) => println!("PDF作成完了: output.pdf (全{}ページ)", page_length),
        Err(e) => {
            eprintln!("PDFファイルの保存に失敗しました: {}", e);
            return Err(e.into());
        }
    };

    Ok(())
}

main.rsとして書き込んでcargo runすると、プロジェクトルートにoutput.pdfが生成される。
この時、main.rsmain.rsの中身をテキストとしてpdfに書き込む。

output.pdfのサンプル

kuro-140kuro-140

Jupyter Notebookの中身の構造を調べてみると以下の記事が見つかった。
https://nbformat.readthedocs.io/en/latest/format_description.html#notebook-file-format
これをLLMに読み込ませてから、内部構造を表現する構造体をRustで定義してもらった。

内部構造を表現した構造体
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::path::PathBuf;

/// マルチライン文字列対応:単一文字列 or 文字列ベクタ
#[derive(Debug, Deserialize, Serialize)]
#[serde(untagged)]
pub enum Source {
    Single(String),
    Multi(Vec<String>),
}

/// Notebook 全体
#[derive(Debug, Deserialize, Serialize)]
pub struct Notebook {
    pub metadata: NotebookMetadata,
    pub nbformat: u8,
    pub nbformat_minor: u8,
    pub cells: Vec<Cell>,
}

/// Notebook レベルのメタデータ
#[derive(Debug, Deserialize, Serialize)]
pub struct NotebookMetadata {
    pub kernelspec: Option<KernelSpec>,
    pub language_info: Option<LanguageInfo>,
    pub authors: Option<Vec<Author>>, // 追加:複数の著者情報 :contentReference[oaicite:6]{index=6}
    #[serde(flatten)]
    pub other: HashMap<String, Value>, // 拡張用フィールド
}

/// 著者情報
#[derive(Debug, Deserialize, Serialize)]
pub struct Author {
    pub name: String, // 必須
    #[serde(flatten)]
    pub other: HashMap<String, Value>, // 追加情報を許容
}

/// セルの種別ごとに分岐
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "cell_type")]
pub enum Cell {
    /// Markdown セル
    #[serde(rename_all = "lowercase")]
    Markdown {
        id: String, // セル ID (4.5+) :contentReference[oaicite:7]{index=7}
        metadata: Value,
        source: Source, // String | Vec<String> :contentReference[oaicite:8]{index=8}
        attachments: Option<HashMap<String, MimeBundle>>,
    },
    /// Code セル
    #[serde(rename_all = "lowercase")]
    Code {
        id: String,
        execution_count: Option<u32>,
        metadata: Value,
        source: Source,
        outputs: Vec<Output>,
    },
    /// Raw NBConvert セル
    #[serde(rename_all = "lowercase")]
    Raw {
        id: String,
        metadata: RawMetadata, // format フィールドを含む :contentReference[oaicite:9]{index=9}
        source: Source,
        attachments: Option<HashMap<String, MimeBundle>>,
    },
}

/// セル添付データ(Mime-bundle)
pub type MimeBundle = HashMap<String, String>;

/// Raw セル固有メタデータ
#[derive(Debug, Deserialize, Serialize)]
pub struct RawMetadata {
    pub format: Option<String>, // 対象 mime-type :contentReference[oaicite:10]{index=10}
    #[serde(flatten)]
    pub other: HashMap<String, Value>,
}

/// コードセル出力の種別
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "output_type", rename_all = "snake_case")]
pub enum Output {
    /// 標準ストリーム出力
    Stream {
        name: String,
        text: Source, // String | Vec<String> :contentReference[oaicite:11]{index=11}
    },
    /// rich display (display_data)
    DisplayData {
        data: HashMap<String, Value>,
        metadata: Value,
    },
    /// 実行結果 (execute_result)
    ExecuteResult {
        execution_count: u32, // 必須 :contentReference[oaicite:12]{index=12}
        data: HashMap<String, Value>,
        metadata: Value,
    },
    /// エラー出力
    Error {
        ename: String,
        evalue: String,
        traceback: Vec<String>,
    },
}

/// kernelspec 情報
#[derive(Debug, Deserialize, Serialize)]
pub struct KernelSpec {
    pub name: String,
    pub display_name: String,
    #[serde(flatten)]
    pub other: HashMap<String, Value>,
}

/// 言語情報
#[derive(Debug, Deserialize, Serialize)]
pub struct LanguageInfo {
    pub name: String,
    pub version: Option<String>,
    pub mimetype: Option<String>,
    #[serde(flatten)]
    pub other: HashMap<String, Value>,
}
kuro-140kuro-140

出力された構造体に使われているRustの構文について調査

  • enum : 複数の型をひとまとまりにした列挙型

  • #[derive(Debug, Deserialize, Serialize)] : あらかじめ定義されたマクロ(デリバティブ)でトレイと実装を自動生成

  • @[serde(untagged)] : Serdeの「タグなし(untagged)列挙型」表現を利用し、JSON側に明示的なタグがなくても複数のバリアントをマッチングしてくれる


enumはわかる。
その他2つがわからないので、調査。

#[derive(Debug, Deserialize, Serialize)]

  • デリバティブ(derive)属性:指定したトレイとの標準的な実装を自動で生成する仕組み。
  • 例えばDebugやCloneなど、よく使われてるトレイとの実装を手書き不要で付与できる。
  • 公式ドキュメント : https://doc.rust-lang.org/reference/attributes/derive.html

要するに新しく作る構造体などのカスタム定義型にトレイトを付与するというだけ。
#[]の書き方は属性(Attribute)と呼ばれる。
Rustの#[]構文は属性(Attribute)を記述するためのもの。属性は、構造体・列挙型・関数・モジュールなどアイテムに付与できるメタデータで、コンパイル時のふるまいを変えたりマクロを呼び出したりするのに使われる。

? 属性とはなにか
定義:属性は「自由形式のメタデータ」で、名前や慣習、コンパイラのバージョンによって解釈が変わる。
構文:#[...]は外部属性(router attribute) -> 直後のアイテムに適用される。#[...]は内側属性(inner attribute) -> 囲んでいるモジュールやクレート全体に適用される。

#[serde(untagged)]の中身はメタアイテム(MetaItem)

  • 属性内部のserde(untagged)はメタアイテムと呼ばれる。
    • パス:serde
    • 引数トークンツリー : (untagged)
      上記の形で構成される

属性全体は次のように文法的に扱われる。

outer-attribute :: = "#[" meta-item-list "]"
meta-tem-list ::= meta-item {"," meta-item }
meta-item ::= path [ "=" literal ] | path "(" token-tree ")" | literal

Attributesに関する公式ドキュメント
https://doc.rust-lang.org/reference/attributes.html

deriveやserdeは属性マクロ(Attribute Macros)の一種

  • デリバティブマクロ (derive Macros)

    • #[derive(Debug, Clone)]はあらかじめ定義されたマクロで、対象の型にDebugCloneのトレイト実装を自動生成する。
  • 属性マクロ(Attribute Macros)

    • #[serde(...)]のように、属性そのものをRustコードで定義・展開できるマクロもある。これらは「プロシージャルマクロ」の一種。
    • 関数様マクロ(custom!(...))
    • デリブマクロ(#[derive(custom)])
    • 属性マクロ(#[cusom])

以上より、公式ドキュメントで探すべきキーワード

  • Attributes
  • outer attribute / inner attribute
  • meta-item syntax
  • Procedural Macros
kuro-140kuro-140

Debug, Deserialize, Serialize Traitについて

  • Debug : トレイトを実装すると {:?}{:#?}フォーマッタを使って型の内部状態をプログラマ向けの文字列表現として出力できるようになる。
  • Serialize : トレイトを実装すると、その型をJSONやTOML, CBOR などSerde対応の任意のデータ形式にシリアライズ(直列化)できるようになり、自動生成マクロで実装が付与される。
  • Deserizlize : このトレイトを実装すると、外部データ(例:JSON文字列)から型を完全に復元(デシリアライズ)でき、借用データのライフタイム(`de)も管理される。