class PDF::Margins::Checker

Constants

DEFAULT_RESOLUTION
MM_TO_PTS
RESOLUTION
SCALE_MULTIPLIER

If we find that we need higher resolution for better precision then we can adjust the SCALE_MULTIPLIER at the cost of speed.

Attributes

bottom_margin[R]
file_path[R]
left_margin[R]
logger[R]
right_margin[R]
spreads[R]
top_margin[R]

Public Class Methods

new(file_path, options = {}) click to toggle source

Dimensions are in mm, to be converted to PDF points later. Pass spreads as true to check left and right margins of spreads, not pages.

# File lib/pdf/margins/checker.rb, line 24
def initialize(file_path, options = {})
  @file_path     = file_path
  @keep_pngs     = options.delete(:keep_pngs) || false

  @top_margin    = options.delete(:top_margin) || 0
  @right_margin  = options.delete(:right_margin) || 0
  @bottom_margin = options.delete(:bottom_margin) || 0
  @left_margin   = options.delete(:left_margin) || 0
  @spreads       = options.delete(:spreads) || false

  @logger = options.delete(:logger) || Logger.new(STDOUT).tap do |l|
    l.level = options.delete(:log_level) || Logger::WARN
  end
end

Public Instance Methods

issues() click to toggle source
# File lib/pdf/margins/checker.rb, line 39
def issues
  temp_dir_path = Dir.mktmpdir("pdf_margins")
  logger.debug("Rendering PNGs into #{temp_dir_path}")

  begin
    # This produces greyscale PNGs - we throw the colour away because we
    # don't need it to check for margins.
    system("mudraw -g -b 0 -r #{RESOLUTION} -o #{temp_dir_path}/%d.png #{file_path}") || raise(PDF::Margins::MuDrawCommandError)

    issues = []

    files = Dir.glob("#{temp_dir_path}/*.png")
    file_count = files.length
    # ensure the files are sorted naturally
    files = files.sort_by{ |f| f.split('/').last.to_i }

    logger.debug("Rendered #{file_count} files")

    files.each_with_index do |png_path, index|
      page_number = index + 1
      logger.debug("Rendering page #{page_number} from #{png_path}")

      image = ChunkyPNG::Image.from_file(png_path)

      if dirty_top_margin?(image, top_margin)
        issues << Issue.new(page_number, :top)
      end

      if dirty_bottom_margin?(image, bottom_margin)
        issues << Issue.new(page_number, :bottom)
      end

      if (!spreads || page_number % 2 == 0) && dirty_left_margin?(image, left_margin)
        issues << Issue.new(page_number, :left)
      end
      
      if (!spreads || page_number % 2 != 0) && dirty_right_margin?(image, right_margin)
        issues << Issue.new(page_number, :right)
      end
    end

  ensure
    FileUtils.remove_entry(temp_dir_path) unless @keep_pngs
    logger.info("PNG files kept at: #{temp_dir_path}") if @keep_pngs
  end

  return issues
end

Private Instance Methods

dirty_bottom_margin?(image, mm) click to toggle source
# File lib/pdf/margins/checker.rb, line 110
def dirty_bottom_margin?(image, mm)
  px = mm_to_pixels(mm)
  offset = image.height - px - 1
  dirty_pixels?(image, 0, offset, image.width, px)
end
dirty_left_margin?(image, mm) click to toggle source
# File lib/pdf/margins/checker.rb, line 99
def dirty_left_margin?(image, mm)
  px = mm_to_pixels(mm)
  dirty_pixels?(image, 0, 0, px, image.height)
end
dirty_pixels?(image, x, y, width, height) click to toggle source
# File lib/pdf/margins/checker.rb, line 116
def dirty_pixels?(image, x, y, width, height)
  white = ChunkyPNG::Color::WHITE
  found_dirty_pixel = false

  width.times do |x_offset|
    break if found_dirty_pixel

    height.times do |y_offset|
      x_position = x + x_offset
      y_position = y + y_offset
      pixel      = image[x_position, y_position]

      if pixel != white 
        hex = ChunkyPNG::Color.to_hex(pixel)
        logger.debug("Found #{hex} pixel at #{x_position},#{y_position}")
        found_dirty_pixel = true
        break
      end
    end
  end

  return found_dirty_pixel
end
dirty_right_margin?(image, mm) click to toggle source
# File lib/pdf/margins/checker.rb, line 104
def dirty_right_margin?(image, mm)
  px = mm_to_pixels(mm)
  offset = image.width - px - 1
  dirty_pixels?(image, offset, 0, px, image.height)
end
dirty_top_margin?(image, mm) click to toggle source
# File lib/pdf/margins/checker.rb, line 94
def dirty_top_margin?(image, mm)
  px = mm_to_pixels(mm)
  dirty_pixels?(image, 0, 0, image.width, px)
end
mm_to_pixels(mm) click to toggle source
# File lib/pdf/margins/checker.rb, line 90
def mm_to_pixels(mm)
  (mm * MM_TO_PTS * SCALE_MULTIPLIER).floor
end