Class: Graphomaton::Exporters::Pdf

Inherits:
Object
  • Object
show all
Includes:
Graphomaton::ExporterIntrospection
Defined in:
lib/graphomaton/exporters/pdf.rb

Defined Under Namespace

Classes: ConversionError

Constant Summary collapse

PDF_SIGNATURE =
'%PDF-'
DEFAULT_CONVERTER =
:auto
DEFAULT_TIMEOUT =
ProcessRunner::DEFAULT_TIMEOUT
DEFAULT_MAX_OUTPUT_BYTES =
ProcessRunner::DEFAULT_MAX_STDOUT_BYTES
CONVERTER_COMMANDS =
{
  rsvg: ['rsvg-convert', '--format', 'pdf', '-'],
  magick: ['magick', 'svg:-', 'pdf:-'],
  convert: ['convert', 'svg:-', 'pdf:-']
}.freeze
CONVERTER_OPTIONS =
([:auto] + CONVERTER_COMMANDS.keys).freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Graphomaton::ExporterIntrospection

#capabilities, #losses_for

Constructor Details

#initialize(automaton) ⇒ Pdf

Returns a new instance of Pdf.



37
38
39
# File 'lib/graphomaton/exporters/pdf.rb', line 37

def initialize(automaton)
  @automaton = automaton
end

Class Method Details

.available?(converter: DEFAULT_CONVERTER) ⇒ Boolean

Returns:

  • (Boolean)


25
26
27
# File 'lib/graphomaton/exporters/pdf.rb', line 25

def self.available?(converter: DEFAULT_CONVERTER)
  !available_command(converter: converter).nil?
end

.available_command(converter: DEFAULT_CONVERTER) ⇒ Object



29
30
31
32
33
34
35
# File 'lib/graphomaton/exporters/pdf.rb', line 29

def self.available_command(converter: DEFAULT_CONVERTER)
  resolved_converter = resolve_converter(converter)
  return CONVERTER_COMMANDS[resolved_converter] if resolved_converter != :auto && executable?(CONVERTER_COMMANDS[resolved_converter].first)
  return nil if resolved_converter != :auto

  CONVERTER_COMMANDS.values.find { |command| executable?(command.first) }
end

.executable?(command) ⇒ Boolean

Returns:

  • (Boolean)


90
91
92
# File 'lib/graphomaton/exporters/pdf.rb', line 90

def self.executable?(command)
  !ProcessRunner.which(command).nil?
end

.resolve_converter(converter) ⇒ Object

Raises:

  • (ArgumentError)


94
95
96
97
98
99
# File 'lib/graphomaton/exporters/pdf.rb', line 94

def self.resolve_converter(converter)
  resolved = converter.to_sym
  return resolved if CONVERTER_OPTIONS.include?(resolved)

  raise ArgumentError, "Unknown PDF converter: #{converter.inspect}. Available converters: #{CONVERTER_OPTIONS.join(', ')}"
end

Instance Method Details

#export(width = 800, height = 600, theme: Svg::DEFAULT_THEME, converter: DEFAULT_CONVERTER, timeout: DEFAULT_TIMEOUT, max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES, **svg_options) ⇒ Object



41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/graphomaton/exporters/pdf.rb', line 41

def export(width = 800, height = 600, theme: Svg::DEFAULT_THEME, converter: DEFAULT_CONVERTER,
           timeout: DEFAULT_TIMEOUT, max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES, **svg_options)
  export_result(
    width,
    height,
    theme: theme,
    converter: converter,
    timeout: timeout,
    max_output_bytes: max_output_bytes,
    **svg_options
  ).output
end

#export_result(width = 800, height = 600, theme: Svg::DEFAULT_THEME, converter: DEFAULT_CONVERTER, timeout: DEFAULT_TIMEOUT, max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES, **svg_options) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/graphomaton/exporters/pdf.rb', line 54

def export_result(width = 800, height = 600, theme: Svg::DEFAULT_THEME, converter: DEFAULT_CONVERTER,
                  timeout: DEFAULT_TIMEOUT, max_output_bytes: DEFAULT_MAX_OUTPUT_BYTES, **svg_options)
  command = available_command(converter: converter)
  raise ConversionError, missing_converter_message(converter) unless command

  svg_result = Svg.new(@automaton).export_result(width, height, theme: theme, **svg_options)
  pdf, error, status = ProcessRunner.capture3(
    *command,
    stdin_data: svg_result.output,
    binmode: true,
    timeout: timeout,
    max_stdout_bytes: max_output_bytes
  )
  pdf = pdf.b

  if status.success? && pdf.start_with?(PDF_SIGNATURE)
    return RenderResult.new(
      output: pdf.freeze,
      diagnostics: svg_result.diagnostics,
      bounds: svg_result.bounds,
      layout: svg_result.layout
    )
  end
  raise ConversionError, invalid_pdf_message(command, error) if status.success?

  raise ConversionError, failed_conversion_message(command, error)
rescue ProcessRunner::Error => e
  raise ConversionError, failed_conversion_message(command, e.message)
end