Module: Sevgi::Sundries::Export

Extended by:
Export
Included in:
Export
Defined in:
lib/sevgi/sundries/export.rb,
lib/sevgi/sundries/export/native.rb,
lib/sevgi/sundries/export/system.rb

Overview

Exports SVG content and post-processes PDF output.

Native PDF/PNG rendering is loaded lazily so installing sevgi-sundries for SVG-only helpers does not require the Cairo, RSVG, or HexaPDF gems. Native export entrypoints raise MissingComponentError when those optional gems are unavailable.

Defined Under Namespace

Modules: Renderer

Constant Summary collapse

AVAILABLE =

Supported export format names mapped to file extensions.

(EXTENSIONS = {
".pdf" => :pdf,
".png" => :png
      }.freeze)
.invert
.freeze
DEFAULT_DPI =

Default SVG CSS pixel density.

96.0
ExportError =

Raised when SVG export or PDF post-processing cannot be completed.

Class.new(Error)

Instance Method Summary collapse

Instance Method Details

#a5_on_a4(infile, outfile) ⇒ Sevgi::Function::Shell::Result

Places a single A5 PDF page twice on an A4 landscape sheet with pdfcpu.

Parameters:

  • infile (String)

    source PDF file path

  • outfile (String)

    destination PDF file path

Returns:

  • (Sevgi::Function::Shell::Result)

    command result

Raises:

  • (Sevgi::Error)

    when pdfcpu is missing or the command fails

  • (Errno::ENOENT)

    when the executable cannot be spawned

See Also:



16
# File 'lib/sevgi/sundries/export/system.rb', line 16

def a5_on_a4(infile, outfile) = F.sh!("pdfcpu", "nup", "--", "form:A4L, border:off", outfile, "2", infile)

#a5_on_a4!(infile) ⇒ void

This method returns an undefined value.

Replaces a PDF file with an A5-on-A4 layout generated by pdfcpu.

Parameters:

  • infile (String)

    PDF file path to modify

Raises:

  • (Sevgi::Error)

    when pdfcpu is missing or the command fails

  • (Errno::ENOENT)

    when the executable cannot be spawned



23
24
25
26
27
28
29
# File 'lib/sevgi/sundries/export/system.rb', line 23

def a5_on_a4!(infile)
  temp = Tempfile.new(%w[output .pdf], File.dirname(infile))
  a5_on_a4(infile, temp.path)
  FileUtils.mv(temp.path, infile)
ensure
  temp&.close!
end

#call(svg, output, format: nil, width: nil, height: nil, dpi: DEFAULT_DPI, css: nil) {|svg| ... } ⇒ Object

Exports SVG source to a PDF or PNG file using librsvg and Cairo.

Parameters:

  • svg (String)

    SVG source content

  • output (String, #to_s)

    output file path

  • format (Symbol, String, nil) (defaults to: nil)

    explicit output format, or nil to infer from output extension

  • width (Numeric, nil) (defaults to: nil)

    target width in output pixels for PNG, or CSS pixels before PDF point conversion

  • height (Numeric, nil) (defaults to: nil)

    target height in output pixels for PNG, or CSS pixels before PDF point conversion

  • dpi (Numeric) (defaults to: DEFAULT_DPI)

    CSS pixel density used for absolute SVG units and PDF point conversion

  • css (String, nil) (defaults to: nil)

    CSS inserted before the closing svg tag before rendering

Yields:

  • (svg)

    optional source transformation applied before rendering

Yield Parameters:

  • svg (String)

    SVG source after optional CSS injection

Yield Returns:

  • (String)

    SVG source to render

Returns:

  • (Object)

    the original output argument

Raises:

  • (Sevgi::ArgumentError)

    when SVG content is not a string or output is blank

  • (Sevgi::Sundries::Export::ExportError)

    when format, SVG parsing, SVG dimensions, or render dimensions are invalid



54
# File 'lib/sevgi/sundries/export.rb', line 54

def call(*args, **kwargs, &block) = native!.call(*args, **kwargs, &block)

#format_for!(format, output) ⇒ Symbol

Resolves the export format from an explicit value or output extension.

Parameters:

  • format (Symbol, String, nil)

    explicit format

  • output (String, #to_s)

    output path

Returns:

  • (Symbol)

    resolved format

Raises:



61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/sevgi/sundries/export.rb', line 61

def format_for!(format, output)
  if format
    format = format.to_sym
    ExportError.("Unsupported export format: #{format}") unless AVAILABLE.key?(format)

    format
  else
    ext = File.extname(output.to_s).downcase
    ExportError.("Unrecognized file extension: #{ext}") unless EXTENSIONS.key?(ext)

    EXTENSIONS[ext]
  end
end

#inject(svg, css) ⇒ String

Inserts CSS before the closing svg tag.

Parameters:

  • svg (String)

    SVG source content

  • css (String)

    CSS source content

Returns:

  • (String)

    SVG source with an added style element when a closing svg tag is present

Raises:

  • (NoMethodError)

    when SVG content does not support string substitution



80
# File 'lib/sevgi/sundries/export.rb', line 80

def inject(svg, css) = svg.sub("</svg>", "<style>#{css}</style></svg>")

#inkscape(infile, outfile = nil, format: nil, background: "#ffffff", opacity: 1, width: nil, height: nil, id: nil, page: nil, css: nil) ⇒ Sevgi::Function::Shell::Result

Exports an SVG file through Inkscape.

Parameters:

  • infile (String)

    source SVG file path

  • outfile (String, nil) (defaults to: nil)

    output path, defaulting to infile with a .pdf extension

  • format (Symbol, String, nil) (defaults to: nil)

    explicit output format, or nil to infer from outfile

  • background (String, nil) (defaults to: "#ffffff")

    export background color

  • opacity (Numeric, nil) (defaults to: 1)

    export background opacity

  • width (Numeric, nil) (defaults to: nil)

    target export width

  • height (Numeric, nil) (defaults to: nil)

    target export height

  • id (String, nil) (defaults to: nil)

    SVG element id to export

  • page (Integer, String, nil) (defaults to: nil)

    page selector passed to Inkscape

  • css (String, nil) (defaults to: nil)

    CSS inserted before exporting

Returns:

  • (Sevgi::Function::Shell::Result)

    command result

Raises:

  • (Sevgi::Sundries::Export::ExportError)

    when format or output extension is unsupported

  • (Sevgi::Error)

    when Inkscape is missing or the command fails

  • (Errno::ENOENT)

    when the executable cannot be spawned

See Also:



47
48
49
50
51
52
53
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
83
84
85
86
87
88
89
# File 'lib/sevgi/sundries/export/system.rb', line 47

def inkscape(
  infile,
  outfile = nil,
  format: nil,
  background: "#ffffff",
  opacity: 1,
  width: nil,
  height: nil,
  id: nil,
  page: nil,
  css: nil
)
  infile = File.expand_path(infile)
  outfile ||= F.subext(".pdf", infile)
  outfile = File.expand_path(outfile)
  format = format_for!(format, outfile)

  if css
    temp = Tempfile.new(%w[input .svg], File.dirname(infile))
    ::File.write(temp.path, inject(::File.read(infile), css))
    infile = temp.path
  end

  F.sh!(
    *[
      "inkscape",
      "--batch-process",
      "--actions=select-by-class:text,object-to-path",
      "--export-type=#{format}",
      ("--export-background=#{background}" if background),
      ("--export-background-opacity=#{opacity}" if opacity),
      ("--export-width=#{width}" if width),
      ("--export-height=#{height}" if height),
      ("--export-id=#{id}" if id),
      ("--export-id-only" if id),
      ("--export-page=#{page}" if page),
      "--export-filename=#{outfile}",
      infile
    ].compact
  )
ensure
  temp&.close!
end

#rsvg(infile, outfile = nil, format: nil, background: "#ffffff", width: nil, height: nil, id: nil, css: nil) ⇒ Sevgi::Function::Shell::Result

Exports an SVG file through rsvg-convert.

Parameters:

  • infile (String)

    source SVG file path

  • outfile (String, nil) (defaults to: nil)

    output path, defaulting to infile with a .pdf extension

  • format (Symbol, String, nil) (defaults to: nil)

    explicit output format, or nil to infer from outfile

  • background (String, nil) (defaults to: "#ffffff")

    export background color

  • width (Numeric, nil) (defaults to: nil)

    target export width

  • height (Numeric, nil) (defaults to: nil)

    target export height

  • id (String, nil) (defaults to: nil)

    SVG element id to export

  • css (String, nil) (defaults to: nil)

    CSS inserted before exporting

Returns:

  • (Sevgi::Function::Shell::Result)

    command result

Raises:

  • (Sevgi::Sundries::Export::ExportError)

    when format or output extension is unsupported

  • (Sevgi::Error)

    when rsvg-convert is missing or the command fails

  • (Errno::ENOENT)

    when the executable cannot be spawned

See Also:



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/sevgi/sundries/export/system.rb', line 105

def rsvg(
  infile,
  outfile = nil,
  format: nil,
  background: "#ffffff",
  width: nil,
  height: nil,
  id: nil,
  css: nil
)
  infile = File.expand_path(infile)
  outfile ||= F.subext(".pdf", infile)
  outfile = File.expand_path(outfile)
  format = format_for!(format, outfile)

  if css
    temp = Tempfile.new(%w[input .svg], File.dirname(infile))
    ::File.write(temp.path, inject(::File.read(infile), css))
    infile = temp.path
  end

  F.sh!(
    *[
      "rsvg-convert",
      "--format=#{format}",
      ("--background-color=#{background}" if background),
      ("--width=#{width}" if width),
      ("--height=#{height}" if height),
      ("--export-id=#{id}" if id),
      "--output=#{outfile}",
      infile
    ].compact
  )
ensure
  temp&.close!
end

#stamp(infile, outfile, stamp:, placeholder:) ⇒ Boolean

Replaces exact placeholder text objects in PDF streams. The placeholder must appear as a PDF literal string in a white text object matching the export stamp pattern. Replacement text is escaped as a PDF literal string. When no exact match is replaced, the output file is not written.

Parameters:

  • infile (String)

    source PDF file path

  • outfile (String)

    destination PDF file path

  • stamp (String)

    replacement text

  • placeholder (String)

    placeholder text to replace

Returns:

  • (Boolean)

    true when at least one matching placeholder was replaced

Raises:



90
# File 'lib/sevgi/sundries/export.rb', line 90

def stamp(infile, outfile, stamp:, placeholder:) = native!.stamp(infile, outfile, stamp:, placeholder:)

#stamp!(infile, stamp:, placeholder:) ⇒ Boolean

Replaces exact placeholder text objects inside a PDF file in place. The input file is replaced only after at least one exact placeholder match is rewritten into a non-empty output file.

Parameters:

  • infile (String)

    PDF file path to modify

  • stamp (String)

    replacement text

  • placeholder (String)

    placeholder text to replace

Returns:

  • (Boolean)

    true when at least one matching placeholder was replaced

Raises:



99
# File 'lib/sevgi/sundries/export.rb', line 99

def stamp!(infile, stamp:, placeholder:) = native!.stamp!(infile, stamp:, placeholder:)

#unite(sources, outfile) ⇒ Sevgi::Function::Shell::Result

Merges PDF files with pdfunite.

Parameters:

  • sources (Array<String>)

    source PDF file paths

  • outfile (String)

    destination PDF file path

Returns:

  • (Sevgi::Function::Shell::Result)

    command result

Raises:

  • (Sevgi::Error)

    when pdfunite is missing or the command fails

  • (Errno::ENOENT)

    when the executable cannot be spawned

See Also:



149
# File 'lib/sevgi/sundries/export/system.rb', line 149

def unite(sources, outfile) = F.sh!("pdfunite", *sources, outfile)