module Ferrum::Page::Screenshot

Constants

DEFAULT_PDF_OPTIONS
PAPER_FORMATS
STREAM_CHUNK

Public Instance Methods

document_size() click to toggle source
# File lib/ferrum/page/screenshot.rb, line 65
      def document_size
        evaluate <<~JS
          [document.documentElement.scrollWidth,
           document.documentElement.scrollHeight]
        JS
      end
mhtml(path: nil) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 53
def mhtml(path: nil)
  data = command("Page.captureSnapshot", format: :mhtml).fetch("data")
  return data if path.nil?
  save_file(path, data)
end
pdf(**opts) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 41
def pdf(**opts)
  path, encoding = common_options(**opts)
  options = pdf_options(**opts).merge(transferMode: "ReturnAsStream")
  handle = command("Page.printToPDF", **options).fetch("stream")

  if path
    stream_to_file(handle, path: path)
  else
    stream_to_memory(handle, encoding: encoding)
  end
end
screenshot(**opts) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 31
def screenshot(**opts)
  path, encoding = common_options(**opts)
  options = screenshot_options(path, **opts)
  data = capture_screenshot(options, opts[:full], opts[:background_color])
  return data if encoding == :base64

  bin = Base64.decode64(data)
  save_file(path, bin)
end
viewport_size() click to toggle source
# File lib/ferrum/page/screenshot.rb, line 59
      def viewport_size
        evaluate <<~JS
          [window.innerWidth, window.innerHeight]
        JS
      end

Private Instance Methods

capture_screenshot(options, full, background_color) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 175
def capture_screenshot(options, full, background_color)
  maybe_resize_fullscreen(full) do
    with_background_color(background_color) do
      command("Page.captureScreenshot", **options)
    end
  end.fetch("data")
end
common_options(encoding: :base64, path: nil, **_) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 102
def common_options(encoding: :base64, path: nil, **_)
  encoding = encoding.to_sym
  encoding = :binary if path
  [path, encoding]
end
get_bounding_rect(selector) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 158
def get_bounding_rect(selector)
  rect = evaluate_async(%Q(
    const rect = document
                   .querySelector('#{selector}')
                   .getBoundingClientRect();
    const {x, y, width, height} = rect;
    arguments[0]([x, y, width, height])
  ), timeout)

  { x: rect[0], y: rect[1], width: rect[2], height: rect[3] }
end
maybe_resize_fullscreen(full) { || ... } click to toggle source
# File lib/ferrum/page/screenshot.rb, line 183
def maybe_resize_fullscreen(full)
  if full
    width, height = viewport_size.dup
    resize(fullscreen: true)
  end

  yield
ensure
  resize(width: width, height: height) if full
end
pdf_options(**opts) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 108
def pdf_options(**opts)
  format = opts.delete(:format)
  options = DEFAULT_PDF_OPTIONS.merge(opts)

  if format
    if opts[:paper_width] || opts[:paper_height]
      raise ArgumentError, "Specify :format or :paper_width, :paper_height"
    end

    dimension = PAPER_FORMATS.fetch(format)
    options.merge!(paper_width: dimension[:width],
                   paper_height: dimension[:height])
  end

  options.map { |k, v| [to_camel_case(k), v] }.to_h
end
save_file(path, data) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 74
def save_file(path, data)
  return data unless path
  File.open(path.to_s, "wb") { |f| f.write(data) }
end
screenshot_options(path = nil, format: nil, scale: 1.0, **opts) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 125
def screenshot_options(path = nil, format: nil, scale: 1.0, **opts)
  options = {}

  format ||= path ? File.extname(path).delete(".") : "png"
  format = "jpeg" if format == "jpg"
  raise "Not supported options `:format` #{format}. jpeg | png" if format !~ /jpeg|png/i
  options.merge!(format: format)

  options.merge!(quality: opts[:quality] ? opts[:quality] : 75) if format == "jpeg"

  if !!opts[:full] && opts[:selector]
    warn "Ignoring :selector in #screenshot since full: true was given at #{caller(1..1).first}"
  end

  if !!opts[:full]
    width, height = document_size
    options.merge!(clip: { x: 0, y: 0, width: width, height: height, scale: scale }) if width > 0 && height > 0
  elsif opts[:selector]
    options.merge!(clip: get_bounding_rect(opts[:selector]).merge(scale: scale))
  end

  if scale != 1.0
    if !options[:clip]
      width, height = viewport_size
      options[:clip] = { x: 0, y: 0, width: width, height: height }
    end

    options[:clip].merge!(scale: scale)
  end

  options
end
stream_to(handle, output) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 90
def stream_to(handle, output)
  loop do
    result = command("IO.read", handle: handle, size: STREAM_CHUNK)

    data_chunk = result["data"]
    data_chunk = Base64.decode64(data_chunk) if result["base64Encoded"]
    output << data_chunk

    break if result["eof"]
  end
end
stream_to_file(handle, path:) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 79
def stream_to_file(handle, path:)
  File.open(path, "wb") { |f| stream_to(handle, f) }
  true
end
stream_to_memory(handle, encoding:) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 84
def stream_to_memory(handle, encoding:)
  data = String.new("") # Mutable string has << and compatible to File
  stream_to(handle, data)
  encoding == :base64 ? Base64.encode64(data) : data
end
to_camel_case(option) click to toggle source
# File lib/ferrum/page/screenshot.rb, line 170
def to_camel_case(option)
  return :preferCSSPageSize if option == :prefer_css_page_size
  option.to_s.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.to_sym
end
with_background_color(color) { || ... } click to toggle source
# File lib/ferrum/page/screenshot.rb, line 194
def with_background_color(color)
  if color
    raise ArgumentError, "Accept Ferrum::RGBA class only" unless color.is_a?(RGBA)

    command("Emulation.setDefaultBackgroundColorOverride", color: color.to_h)
  end

  yield
ensure
  command("Emulation.setDefaultBackgroundColorOverride") if color
end