module MergePDFs

mergepdfs combines multiple PDF files into one output PDF file. Empty pages are added to ensure that each merged PDF file starts at an odd page number in the output file. When printed double-sided, pages of the merged PDF files are never printed on the same sheet.

Copyright © 2020 Huub de Beer <huub@campinecomputing.eu>

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.

Constants

DEFAULT_OPTIONS
DEFAULT_OUTPUT_FILENAME
RELEASE_DATE
VERSION
VERSION_MESSAGE

Public Class Methods

merge(output:, pdfs: [], continue_on_error: false, show_diagnostics: false) click to toggle source

Merge multiple PDF files into a single PDF file, starting each PDF file at an odd page.

@param output [String] the filename to write the merged PDFs to @param pdfs [Array<String>] the filenames of the PDF files to merge @param continue_on_error [Boolean] if an input PDF file cannot be

merged, continue merging the other input PDF files

@param show_diagnostics [Boolean] show more detailed error information

# File lib/mergepdfs.rb, line 55
def self.merge(output:, pdfs: [], continue_on_error: false, show_diagnostics: false)
    merged_pdf = HexaPDF::Document.new

    pdfs.each do |pdf|
        # invariant: merged_pdf.pages.count.even?
        begin
            pdf_file = HexaPDF::Document.open(pdf)
            pdf_file.pages.each do |page| 
                merged_page = merged_pdf.import(page)
                merged_pdf.pages.add(merged_page)
            end
            merged_pdf.pages.add() if pdf_file.pages.count.odd?
        rescue => e
            warn "ERROR\t Unable to open or read '#{pdf}'. Make sure this file is readable, unencrypted, and a PDF file."
            warn e.full_message() if show_diagnostics
            return unless continue_on_error
        end
    end

    begin
        merged_pdf.write(output, optimize: true)
    rescue => e
        warn "ERROR\t Unable to write the merged PDF to the output file '#{output}'."
        warn e.full_message() if show_diagnostics
    end
end
parse(args = ARGV) click to toggle source

Parse command-line parameters

@param args [Array<String>] a list with command-line parameters @return [Hash] a map with options matching the parameters for merge.

# File lib/mergepdfs.rb, line 87
def self.parse(args = ARGV)
    options = DEFAULT_OPTIONS

    begin
        OptionParser.new do |parser|
            parser.banner = "Usage: mergepdfs --output FILENAME [OPTIONS] file1.pdf file2.pdf ... fileN.pdf"

            parser.separator("");
            parser.separator("Required:")

            parser.on("-o FILENAME", "--output FILENAME",
                      "The name of the output PDF file. If no filename is specified,",
                      "'#{DEFAULT_OUTPUT_FILENAME}' is used by default.") do |output|
                options[:output] = output
            end

            parser.separator("");
            parser.separator("Optional:")

            parser.on("-c", "--continue-on-error", 
                      "When an error occurs merging a PDF file, mergepdfs tries",
                      "to continue merging the other PDF files. Default is false.") do
                options[:continue_on_error] = true
            end

            parser.on("-d", "--diagnostics",
                      "Show detailed error messages. Default is false.") do
                options[:show_diagnostics] = true
            end

            parser.separator("");
            parser.separator("Common:")

            parser.on_tail("-v", "--version", "Show the version.") do
                puts VERSION_MESSAGE
                exit
            end

            parser.on_tail("-h", "--help", "Show this help message.") do
                puts parser
                exit
            end
        end.parse!(args)

    rescue OptionParser::InvalidOption => e
        warn "ERROR\t #{e}."
        warn e.full_message if options[:show_diagnostics]
    rescue OptionParser::ParseError => e
        warn "ERROR\t Problem while parsing the command-line parameters: #{e}."
        warn e.full_message if options[:show_diagnostics]
    end

    if not options.key? :output
        options[:output] = DEFAULT_OUTPUT_FILENAME
        warn "ERROR\t No output filename specified. Defaulting to '#{DEFAULT_OUTPUT_FILENAME}'." if options[:show_diagnostics]
    end

    # The rest of the command-line options are treated as the input PDF
    # files.
    options[:pdfs] = args
    options
end