Skip to content

Instantly share code, notes, and snippets.

@fsubal
Last active October 8, 2026 00:23
Show Gist options
  • Select an option

  • Save fsubal/d5e1aebbed3717afc66bb27431c2bf86 to your computer and use it in GitHub Desktop.

Select an option

Save fsubal/d5e1aebbed3717afc66bb27431c2bf86 to your computer and use it in GitHub Desktop.
class PdfgateClient
class Error < StandardError
attr_reader :code
def initialize(code, message)
super("PdfgateClient Error (#{code}): #{message}")
@code = code
end
end
Issue = Data.define(:severity, :code, :message) do
def deny?
severity == "deny"
end
def warn?
severity == "warn"
end
end
EXECUTABLE_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', 'bin', 'pdfgate'))
POLICY_FILE_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..', 'policy', 'default.json'))
def initialize(pdf_file_path, policy_file_path: POLICY_FILE_PATH, timeout: 20.seconds)
@pdf_file_path = pdf_file_path
@policy_file_path = policy_file_path if File.exist?(policy_file_path)
@timeout = timeout
end
def validate!
case result
in [{ ok: true, issues: }, 0]
issues.map { |issue| Issue.new(**issue) }
in [{ ok: false, issues: }, 1]
issues.map { |issue| Issue.new(**issue) }
in [{ error: { code:, message: } }, 2]
raise Error.new(code, message)
else
error = Error.new("parse-failure", "Failed to parse the output of the pdfgate command.")
Sentry.capture_exception(error, extra: { stdout: result[0], exitstatus: result[1] })
raise error
end
end
private
def result
stdout, status = run_with_timeout
[JSON.parse(stdout, symbolize_names: true), status&.exitstatus]
rescue JSON::ParserError
[nil, status&.exitstatus]
end
def pdfgate_arguments
[EXECUTABLE_PATH, "validate", @pdf_file_path].tap do |args|
args.push("--policy", @policy_file_path.to_s) if @policy_file_path
end
end
def run_with_timeout
Open3.popen2(*pdfgate_arguments) do |stdin, stdout, wait_thr|
stdin.close
reader = Thread.new { stdout.read }
if wait_thr.join(@timeout)
[reader.value, wait_thr.value]
else
begin
Process.kill("KILL", wait_thr.pid)
rescue Errno::ESRCH
nil
end
wait_thr.join
[nil, nil]
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment