Module: Pdfrb::Task::VeraPdfCrossCheck
- Defined in:
- lib/pdfrb/task/vera_pdf_cross_check.rb
Overview
Cross-check pdfrb's PDF/A output against the veraPDF validator (the reference implementation for ISO 19005 conformance).
Runs the verapdf binary over a PDF file and parses the XML
report. The binary path is configurable via VERAPDF_BIN or the
binary: argument for CI environments that download a release.
Defined Under Namespace
Constant Summary collapse
- FLAVOURS =
{ a1b: "1b", a1a: "1a", a2b: "2b", a2a: "2a", a2u: "2u", a3b: "3b", a3a: "3a", a3u: "3u", a4b: "4b", a4f: "4f", a4e: "4e", u1a: "ua1", u2a: "ua2", u3a: "ua3" }.freeze
Class Method Summary collapse
-
.available?(binary: ENV.fetch("VERAPDF_BIN", "verapdf")) ⇒ Boolean
True when the verapdf binary is available.
- .call(pdf, flavour: :a2b, binary: ENV.fetch("VERAPDF_BIN", "verapdf")) ⇒ Result
-
.materialise(pdf) ⇒ Object
Returns [path, tempdir-or-nil]; the caller removes tempdir.
- .parse_report(xml) ⇒ Object
Class Method Details
.available?(binary: ENV.fetch("VERAPDF_BIN", "verapdf")) ⇒ Boolean
True when the verapdf binary is available.
57 58 59 60 61 62 |
# File 'lib/pdfrb/task/vera_pdf_cross_check.rb', line 57 def available?(binary: ENV.fetch("VERAPDF_BIN", "verapdf")) _out, _err, status = Open3.capture3(binary, "--version") status.success? rescue Errno::ENOENT false end |
.call(pdf, flavour: :a2b, binary: ENV.fetch("VERAPDF_BIN", "verapdf")) ⇒ Result
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/pdfrb/task/vera_pdf_cross_check.rb', line 44 def call(pdf, flavour: :a2b, binary: ENV.fetch("VERAPDF_BIN", "verapdf")) flavour_code = FLAVOURS.fetch(flavour.to_sym, flavour.to_s) path, tempdir = materialise(pdf) out, err, status = Open3.capture3(binary, "--flavour", flavour_code, path) report = parse_report(out) if status.success? || out.include?("<report") report || raise(Pdfrb::Error, "verapdf failed (#{status.exitstatus}): #{err[0, 200]}") ensure FileUtils.remove_entry(tempdir) if tempdir end |
.materialise(pdf) ⇒ Object
Returns [path, tempdir-or-nil]; the caller removes tempdir.
95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/pdfrb/task/vera_pdf_cross_check.rb', line 95 def materialise(pdf) if pdf.is_a?(String) && !pdf.include?("\0") && File.file?(pdf) return [pdf, nil] end bytes = pdf.is_a?(String) ? pdf : pdf.read require "tmpdir" tempdir = Dir.mktmpdir("verapdf") path = File.join(tempdir, "check.pdf") File.binwrite(path, bytes) [path, tempdir] end |
.parse_report(xml) ⇒ Object
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/pdfrb/task/vera_pdf_cross_check.rb', line 64 def parse_report(xml) require "rexml/document" doc = REXML::Document.new(xml) validation = REXML::XPath.first(doc, "//validationReport") details = REXML::XPath.first(doc, "//validationReport/details") result = Result.new( validation&.[]("isCompliant") == "true", details&.[]("passedRules").to_i, details&.[]("failedRules").to_i, details&.[]("passedChecks").to_i, details&.[]("failedChecks").to_i, [], xml, validation&.[]("profileName").to_s ) REXML::XPath.each(doc, "//rule[@status='failed']") do |rule| description = rule.elements["description"]&.text.to_s.strip clause = "#{rule.attributes['specification']} " \ "cl.#{rule.attributes['clause']}.#{rule.attributes['testNumber']}" REXML::XPath.each(rule, ".//check[@status='failed']") do |check| result.failures << Failure.new( clause, description, check.elements["errorMessage"]&.text.to_s.strip, check.elements["context"]&.text.to_s.strip ) end end result end |