From babbf8162331a5144dfdf38ab5c4fc4b9e9e6024 Mon Sep 17 00:00:00 2001 From: Hollow Man Date: Wed, 9 Feb 2022 01:18:19 +0800 Subject: [PATCH] v0.1.2 Signed-off-by: Hollow Man --- .github/workflows/build.yml | 4 +- .github/workflows/test.yml | 50 +++++++++++++++- Cargo.toml | 30 +++++----- README.md | 6 +- README_CN.md | 14 +++-- src/main.rs | 112 +++++++++++++++++++++++++----------- test_doc/book.toml | 2 + 7 files changed, 156 insertions(+), 62 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4a02ed0..7c6f26b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - version: ["0.1.1"] + version: ["0.1.2"] os: ["macos-latest", "ubuntu-latest", "windows-latest"] runs-on: ${{ matrix.os }} steps: @@ -61,5 +61,5 @@ jobs: - name: Upload Binary File to Artifact uses: actions/upload-artifact@v2 with: - name: mdbook-pdf-v0.1.1-aarch64-unknown-linux-gnu + name: mdbook-pdf-v0.1.2-aarch64-unknown-linux-gnu path: target/release/mdbook-pdf diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e6c2132..531eb52 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,121 +18,145 @@ jobs: dir: "test_doc" name: "An_Example" backend-depends: "" + static-url: "" - os: "ubuntu-latest" repo: "https://github.com/HollowMan6/mdbook-pdf" dir: "test_doc" name: "An_Example" backend-depends: "" + static-url: "" - os: "macos-latest" repo: "https://github.com/rust-lang/cargo" dir: "cargo/src/doc" name: "Cargo_Book" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/cargo/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/cargo" dir: "cargo/src/doc" name: "Cargo_Book" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/cargo/" - os: "macos-latest" repo: "https://github.com/rust-lang/edition-guide" dir: "edition-guide" name: "Edition_Guide" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/edition-guide/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/edition-guide" dir: "edition-guide" name: "Edition_Guide" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/edition-guide/" - os: "macos-latest" repo: "https://github.com/rust-embedded/book" dir: "book" name: "Embedded_Rust_Book" backend-depends: "" + static-url: "https://docs.rust-embedded.org/book/" - os: "ubuntu-latest" repo: "https://github.com/rust-embedded/book" dir: "book" name: "Embedded_Rust_Book" backend-depends: "" + static-url: "https://docs.rust-embedded.org/book/" - os: "macos-latest" repo: "https://github.com/rust-lang/mdBook" dir: "mdBook/guide" name: "Mdbook_User_Guide" backend-depends: "" + static-url: "https://rust-lang.github.io/mdBook/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/mdBook" dir: "mdBook/guide" name: "Mdbook_User_Guide" backend-depends: "" + static-url: "https://rust-lang.github.io/mdBook/" - os: "macos-latest" repo: "https://github.com/rust-lang/mdBook" dir: "mdBook/test_book" name: "Mdbook_Test_Book" backend-depends: "" + static-url: "" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/mdBook" dir: "mdBook/test_book" name: "Mdbook_Test_Book" backend-depends: "" + static-url: "" - os: "macos-latest" repo: "https://github.com/rust-lang/reference" dir: "reference" name: "Rust_Reference" backend-depends: "" + static-url: "https://doc.rust-lang.org/nightly/reference/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/reference" dir: "reference" name: "Rust_Reference" backend-depends: "" + static-url: "https://doc.rust-lang.org/nightly/reference/" - os: "macos-latest" repo: "https://github.com/rust-lang/rust-by-example" dir: "rust-by-example" name: "Rust_By_Example" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/rust-by-example/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/rust-by-example" dir: "rust-by-example" name: "Rust_By_Example" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/rust-by-example/" - os: "macos-latest" repo: "https://github.com/rust-lang/book" dir: "book" name: "Rust_Programming_Language" backend-depends: "" + static-url: "https://doc.rust-lang.org/book/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/book" dir: "book" name: "Rust_Programming_Language" backend-depends: "" + static-url: "https://doc.rust-lang.org/book/" - os: "macos-latest" repo: "https://github.com/rust-lang/rustc-dev-guide" dir: "rustc-dev-guide" name: "Rustc_Book" backend-depends: "mdbook-linkcheck mdbook-toc" + static-url: "https://rustc-dev-guide.rust-lang.org/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/rustc-dev-guide" dir: "rustc-dev-guide" name: "Rustc_Book" backend-depends: "mdbook-linkcheck mdbook-toc" + static-url: "https://rustc-dev-guide.rust-lang.org/" - os: "macos-latest" repo: "https://github.com/rust-lang/rust" dir: "rust/src/doc/rustdoc" name: "Rustdoc_Book" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/rustdoc/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/rust" dir: "rust/src/doc/rustdoc" name: "Rustdoc_Book" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/rustdoc/" - os: "macos-latest" repo: "https://github.com/rust-lang/nomicon" dir: "nomicon" name: "Rust_Nomicon" backend-depends: "" + static-url: "https://doc.rust-lang.org/nomicon/" - os: "ubuntu-latest" repo: "https://github.com/rust-lang/nomicon" dir: "nomicon" name: "Rust_Nomicon" backend-depends: "" + static-url: "https://doc.rust-lang.org/nomicon/" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -167,13 +191,19 @@ jobs: echo "margin-bottom = 0.5" >> book.toml echo "margin-left = 0.5" >> book.toml echo "margin-right = 0.5" >> book.toml + echo "static-site-url = '${{ matrix.static-url }}'" >> book.toml fi mdbook build - name: Upload PDF File to Artifact uses: actions/upload-artifact@v2 with: - name: ${{ runner.os }}_${{ matrix.name }} + name: ${{ runner.os }}_${{ matrix.name }}_PDF path: ${{ matrix.dir }}/book/pdf/*.pdf + - name: Upload Build HTML to Artifact + uses: actions/upload-artifact@v2 + with: + name: ${{ runner.os }}_${{ matrix.name }}_HTML + path: ${{ matrix.dir }}/book/html/* test-win: strategy: @@ -184,42 +214,52 @@ jobs: dir: "cargo/src/doc" name: "Cargo_Book" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/cargo/" - repo: "https://github.com/rust-lang/edition-guide" dir: "edition-guide" name: "Edition_Guide" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/edition-guide/" - repo: "https://github.com/rust-embedded/book" dir: "book" name: "Embedded_Rust_Book" backend-depends: "" + static-url: "https://docs.rust-embedded.org/book/" - repo: "https://github.com/rust-lang/mdBook" dir: "mdBook/guide" name: "Mdbook_User_Guide" backend-depends: "" + static-url: "https://rust-lang.github.io/mdBook/" - repo: "https://github.com/rust-lang/mdBook" dir: "mdBook/test_book" name: "Mdbook_Test_Book" backend-depends: "" + static-url: "" - repo: "https://github.com/rust-lang/reference" dir: "reference" name: "Rust_Reference" backend-depends: "" + static-url: "https://doc.rust-lang.org/nightly/reference/" - repo: "https://github.com/rust-lang/rust-by-example" dir: "rust-by-example" name: "Rust_By_Example" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/rust-by-example/" - repo: "https://github.com/rust-lang/book" dir: "book" name: "Rust_Programming_Language" backend-depends: "" + static-url: "https://doc.rust-lang.org/book/" - repo: "https://github.com/rust-lang/rust" dir: "rust/src/doc/rustdoc" name: "Rustdoc_Book" backend-depends: "" + static-url: "https://doc.rust-lang.org/stable/rustdoc/" - repo: "https://github.com/rust-lang/nomicon" dir: "nomicon" name: "Rust_Nomicon" backend-depends: "" + static-url: "https://doc.rust-lang.org/nomicon/" runs-on: "windows-latest" steps: - uses: actions/checkout@v2 @@ -254,9 +294,15 @@ jobs: echo "margin-bottom = 0.5" >> book.toml echo "margin-left = 0.5" >> book.toml echo "margin-right = 0.5" >> book.toml + echo "static-site-url = '${{ matrix.static-url }}'" >> book.toml mdbook build - name: Upload PDF File to Artifact uses: actions/upload-artifact@v2 with: - name: ${{ runner.os }}_${{ matrix.name }} + name: ${{ runner.os }}_${{ matrix.name }}_PDF path: ${{ matrix.dir }}/book/pdf/*.pdf + - name: Upload Build HTML to Artifact + uses: actions/upload-artifact@v2 + with: + name: ${{ runner.os }}_${{ matrix.name }}_HTML + path: ${{ matrix.dir }}/book/html/* diff --git a/Cargo.toml b/Cargo.toml index afa4e9b..b8aa8ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,21 @@ [package] -name = "mdbook-pdf" -version = "0.1.1" authors = ["Hollow Man "] -edition = "2021" +categories = ["text-processing", "command-line-interface"] description = "A backend for mdBook written in Rust for generating PDF based on headless chrome and Chrome DevTools Protocol" -license = "GPL-3.0" +edition = "2021" homepage = "https://hollowmansblog.wordpress.com/2022/01/30/mdbook-pdf-a-mdbook-backend-for-generating-pdf-files/" -repository = "https://github.com/HollowMan6/mdbook-pdf" keywords = ["book", "mdbook", "rustbook", "pdf"] +license = "GPL-3.0" +name = "mdbook-pdf" readme = "README.md" -categories = ["text-processing", "command-line-interface"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +repository = "https://github.com/HollowMan6/mdbook-pdf" +version = "0.1.2" [dependencies] -# # Wait for automatic chrome download to be available: https://github.com/atroche/rust-headless-chrome/issues/286 -# headless_chrome = { git = "https://github.com/atroche/rust-headless-chrome", branch = "master", features= ["fetch"] } - -# A forked version to expand some response timeout to 300 seconds. -mdbook-pdf-headless_chrome = "0.1.0" -mdbook = "0.4.15" -serde = "1.0.136" -serde_derive = "1.0.136" -env_logger = "0.9.0" +env_logger = "0" +lazy_static = "1" +mdbook = "0" +mdbook-pdf-headless_chrome = "0" +regex = "1" +serde = "1" +serde_derive = "1" diff --git a/README.md b/README.md index a3f975f..8865081 100644 --- a/README.md +++ b/README.md @@ -68,17 +68,19 @@ Finally you can build your book and get the PDF file with `mdbook build` command ## Configuration Support customize PDF paper orientation, scale of the webpage rendering, paper width and height, page margins, generated PDF page ranges, whether to display header and footer as well as customize their formats, and more. -Check [book.toml](test_doc/book.toml#L10-L33) and comments for details for the available configurations of `[output.pdf]`. +Check [book.toml](test_doc/book.toml#L10-L35) and comments for details for the available configurations of `[output.pdf]`. ## Common Issues 1. Support for Firefox in `mdbook-pdf`! Currently, according to [Puppeteer's documentation](https://pptr.dev/#?product=Puppeteer&show=api-pagepdfoptions), [Chrome DevTools Protocol Page.printToPDF](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF) is only supported in Chrome headless. An issue has already been filed for this [here](https://github.com/puppeteer/puppeteer/issues/7628). -2. Broken Table of Contents links! +2. Broken links! I've already submitted [a PR for mdBook](https://github.com/rust-lang/mdBook/pull/1738) to fix this by making print page (print.html) links link to anchors on the print page, but it's not merged yet. You can try [my PR fork](https://github.com/HollowMan6/mdBook) for this to work. +If you have relative links that link outside the book, please provide the [static hosting site URL](test_doc/book.toml#L17-L18) for it to get fixed. + 3. Can you add the bookmark to the PDF reflecting the Table of Contents, just like what [wkhtmltopdf](https://wkhtmltopdf.org/) supported? This should be realized by Chromium, and an issue has already been filed for this [here](https://bugs.chromium.org/p/chromium/issues/detail?id=781797). diff --git a/README_CN.md b/README_CN.md index 3cdf773..614e2a5 100644 --- a/README_CN.md +++ b/README_CN.md @@ -67,7 +67,7 @@ title = "An Example" ## 配置 支持自定义PDF纸张方向、页面缩放比例、纸张宽度和高度、页面边距、生成的PDF页面范围、是否显示页眉和页脚以及自定义其格式等。 -查看 [book.toml](test_doc/book.toml#L10-L33) 以了解 `[output.pdf]` 可用配置的详细信息。 +查看 [book.toml](test_doc/book.toml#L10-L35) 以了解 `[output.pdf]` 可用配置的详细信息。 ### 具体参数详解 - trying-times @@ -80,6 +80,10 @@ title = "An Example" 本程序支持最新的基于Chromium的浏览器,不支持Safari和Firefox。如果你需要指定,请指定完整的路径,比如说`/usr/bin/foo`。如果指定了错误的可执行文件,则很可能会出现超时错误或者直接报错。 +- static_site_url + +接受输入一个字符串,默认为空`''`。其指定书的静态网站托管URL,从而修复书之外的相对链接,将其转换为绝对路径。 + - landscape 接受输入一个布尔值,默认为`false`。其指定PDF纸张方向,`true`为横向,`false`为纵向。 @@ -151,15 +155,17 @@ title = "An Example" ## 常见问题 1. 让`mdbook-pdf`支持火狐! -目前,根据[Puppeteer的文档](https://pptr.dev/#?product=Puppeteer&show=api-pagepdfoptions),[Chrome 开发工具协议 Page.printToPDF](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF) 仅在 Chrome 无头模式中受支持。已经为此提交了一个[议题](https://github.com/puppeteer/puppeteer/issues/7628)。 +目前,根据[Puppeteer的文档](https://pptr.dev/#?product=Puppeteer&show=api-pagepdfoptions),[Chrome 开发工具协议 Page.printToPDF](https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF) 仅在 Chrome 无头模式中受支持。目前已经有人为此提交了一个[议题](https://github.com/puppeteer/puppeteer/issues/7628)。 -2. 目录链接损坏! +2. 链接损坏! 我已经提交了[一个 mdBook 的拉取请求](https://github.com/rust-lang/mdBook/pull/1738),该拉取请求通过将打印页面 (print.html) 上的链接指向打印页面上的锚点来解决此问题,但尚未合并。您可以尝试[我的拉取请求分支](https://github.com/HollowMan6/mdBook) 以使其正常工作。 +如果你的书中有书以外的相对路径链接,请提供[静态网站托管URL](test_doc/book.toml#L17-L18)以便修复。 + 3. 可以像[wkhtmltopdf](https://wkhtmltopdf.org/)支持的那样,在PDF中添加书签来反映目录吗? -这应该由 Chromium 实现,并且已经为此提交了一个[议题](https://bugs.chromium.org/p/chromium/issues/detail?id=781797)。 +这应该由 Chromium 实现,并且目前已经有人为此提交了一个[议题](https://bugs.chromium.org/p/chromium/issues/detail?id=781797)。 4. 无法在 `mdbook-pdf` 中将我的书呈现为 PDF! diff --git a/src/main.rs b/src/main.rs index 271c354..271445d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,11 +16,18 @@ * along with this program. If not, see . */ use headless_chrome::{types::PrintToPdfOptions, Browser, LaunchOptionsBuilder}; +use lazy_static::lazy_static; use mdbook::renderer::RenderContext; -use std::{ffi::OsStr, fs, io, io::Write, path::PathBuf, thread, time::Duration}; +use regex::Regex; +use std::io::{BufReader, BufWriter, Read, Write}; +use std::{ffi::OsStr, fs, io, path::PathBuf, thread, time::Duration}; fn main() -> Result<(), Box> { env_logger::init(); + lazy_static! { + static ref SCHEME_LINK: Regex = Regex::new(r"^[a-z][a-z0-9+.-]*:").unwrap(); + static ref A_LINK: Regex = Regex::new(r#"(]*?href=")([^"]+?)""#).unwrap(); + } // Receives the data passed to the program via mdbook let mut stdin = io::stdin(); @@ -44,44 +51,77 @@ fn main() -> Result<(), Box> { .unwrap() .to_owned(); - // Modify the print.html for custom JS scripts - // (It's OK to append the script outside the html tag) - let mut file = fs::OpenOptions::new() - .append(true) + // Modify the print.html for custom JS scripts as well as links outside the book. + let file = fs::OpenOptions::new() + .read(true) .open(print_html_path.clone()) .unwrap(); - if let Err(e) = writeln!( - file, - " - - -" - ) { - eprintln!("Couldn't write to file: {}", e); + let mut buf_reader = BufReader::new(file); + let mut contents = String::new(); + buf_reader.read_to_string(&mut contents)?; + contents = contents.replacen( + "", + " + + + ", + 1, + ); + if !cfg.static_site_url.is_empty() { + contents = A_LINK + .replace_all(&contents, |caps: ®ex::Captures<'_>| { + // Ensure that there is no '\' in the link to ensure the following judgement work + let link = caps[2].replace("\\", "/"); + // Don't modify links with schemes like `https`, and no need to modify pages inside the book. + if !link.starts_with("#") + && !SCHEME_LINK.is_match(&link) + && (link.starts_with("../") || link.contains("/../")) + { + let mut fixed_link = String::new(); + + fixed_link.push_str(&cfg.static_site_url); + if !fixed_link.ends_with("/") { + fixed_link.push('/'); + } + fixed_link.push_str(&link); + + return format!("{}{}\"", &caps[1], &fixed_link); + } + // Otherwise, leave it as-is. + format!("{}{}\"", &caps[1], &caps[2]) + }) + .into_owned(); } + let file = fs::OpenOptions::new() + .write(true) + .open(print_html_path.clone()) + .unwrap(); + let mut buf_writer = BufWriter::new(file); + buf_writer.write_all(contents.as_bytes())?; + // Used to relieve errors related to timeouts, but now for // the patches in my fork, it's not likely to be needed let trying_times = cfg.trying_times + 1; @@ -178,6 +218,7 @@ extern crate serde_derive; pub struct PrintOptions { pub trying_times: u64, pub browser_binary_path: String, + pub static_site_url: String, pub landscape: bool, pub display_header_footer: bool, pub print_background: bool, @@ -205,6 +246,7 @@ impl Default for PrintOptions { PrintOptions { trying_times: 1u64, browser_binary_path: "".to_string(), + static_site_url: "".to_string(), landscape: false, display_header_footer: false, print_background: false, diff --git a/test_doc/book.toml b/test_doc/book.toml index afd269d..f65a3f9 100644 --- a/test_doc/book.toml +++ b/test_doc/book.toml @@ -14,6 +14,8 @@ title = "An Example" ## If needed, please specify the full path. ## If you specify the wrong binary, chances are that there will be a timeout error. # browser-binary-path = "" +## Assign the static hosting site url so that relative links outside the book can be fixed. +# static_site_url = "https://rust-lang.github.io/mdBook" ## Check Chrome Devtools Protocol Docs for the explanation of the following params: ## https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF landscape = false