Skip to content

Instantly share code, notes, and snippets.

@swistak
Last active January 3, 2016 06:38

Revisions

  1. swistak revised this gist Jan 14, 2014. 1 changed file with 297 additions and 0 deletions.
    297 changes: 297 additions & 0 deletions print.css
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,297 @@
    /*
    * Printing styles based on HTML5 Boilerplate
    *
    * What follows is the result of much research on cross-browser styling.
    * Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal,
    * Kroc Camen, and the H5BP dev community and team.
    *
    * Additional modifications and printing classes by Marcin Raczkowski
    */

    /* ==========================================================================
    Base styles: opinionated defaults + Print styles.
    Inlined to avoid required HTTP connection: h5bp.com/r
    ========================================================================== */

    html,
    body,
    button,
    input,
    select,
    textarea {
    color: #000;/* Black prints faster: h5bp.com/s */
    background-color: #fff;
    }

    html, body {
    font-size: 9pt;
    line-height: 1.4;
    font-family: Georgia, serif;
    }

    @media screen {
    html, body {
    font-size: 10px;
    }
    }

    body {
    /* Print related */
    box-shadow: none !important;
    text-shadow: none !important;
    }

    a,
    a:visited {
    text-decoration: underline;
    }

    a[href]:after {
    content: " (" attr(href) ")";
    }

    abbr[title]:after {
    content: " (" attr(title) ")";
    }

    /*
    * Don't show links for images, or javascript/internal links
    */

    .ir a:after,
    a[href^="javascript:"]:after,
    a[href^="#"]:after {
    content: "";
    }

    pre,
    blockquote {
    border: 1px solid #999;
    page-break-inside: avoid;
    }

    thead {
    display: table-header-group; /* h5bp.com/t */
    }

    tr,
    img {
    page-break-inside: avoid;
    }

    img {
    max-width: 100% !important;
    }

    p,
    h2,
    h3 {
    orphans: 3;
    widows: 3;
    }

    h2,
    h3 {
    page-break-after: avoid;
    }



    /*
    * A better looking default horizontal rule
    */

    hr {
    display: block;
    height: 1px;
    border: 0;
    border-top: 1px solid #ccc;
    margin: 1em 0;
    padding: 0;
    }

    /*
    * Remove the gap between images and the bottom of their containers: h5bp.com/i/440
    */

    img {
    vertical-align: middle;
    }

    /*
    * Remove default fieldset styles.
    */

    fieldset {
    border: 0;
    margin: 0;
    padding: 0;
    }

    /*
    * Allow only vertical resizing of textareas.
    */

    textarea {
    resize: vertical;
    }


    /* ==========================================================================
    Additional print styles. Usefull for previews & document authoring.
    ========================================================================== */

    body {
    margin: 0 auto;
    position: relative;
    }

    body.vertical {
    width: 21cm; /* A4 sized page with 0.5 cm margin/padding */
    padding: 0.5cm 0.5cm 1cm; /* USe these values if smart shrinking is disabled,they must mach margins */
    }

    body.horizontal {
    width: 29.7cm; /* A4(210x297) sized page with 0.5 cm margin top & 1cm margin bottom */
    padding: 0.5cm 0.5cm 1cm;
    }

    body.vertical .page {
    width: 20cm; /* A4 sized page with 0.5 cm margin */
    height: 28.1cm; /* 29.7 height - 0.5 top margin - 1cm bottom margin - 0.1 for rounding errors. */
    }

    body.horizontal .page {
    width: 29.7cm; /* A4 sized page with 0.5 cm margin */
    height: 19.4cm; /* 21 (A4 H) - 0.5 (top margin) - 1 (bottom margin) - 0.1(rounding errors) */
    }

    /* gives us roughly 200x287 or 287x200 cm working area */
    body .page {
    position: relative;
    margin: 0;
    page-break-inside: avoid;
    }

    @media screen {
    body .page {
    overflow: hidden;

    padding: 2px;
    outline: black 1px solid;
    outline-offset: 0.5cm;
    margin-top: 1cm;
    margin-bottom: 1.5cm;
    }
    }

    /* Compact definition list */

    dl.compact {
    margin: 0;
    font-size: 0.9em;
    width: 5cm;
    }

    dl.compact dt {
    font-weight: bold;
    clear: left; float: left;

    width: 2.25cm;
    white-space: nowrap;
    overflow: hidden;
    }
    dl.compact dd {
    float: left;
    margin: 0 0 0 20px;
    white-space: nowrap;
    overflow: hidden;
    }

    /* Fixed width tables + some default table styles */

    table.fixed {
    table-layout: fixed;
    }

    table.fixed td,
    table.fixed th {
    white-space: nowrap;
    overflow: visible;
    word-break: break-all;
    }

    table td,
    table th {
    padding: 0.3em 0.6em;
    border: 1px solid black;
    white-space: nowrap;
    }

    table th {
    padding: 0.3em 0;
    text-align: center;
    }

    table.zebra {
    width: 100%;
    border: 2px solid black;
    }

    table.zebra th { background-color: #aaa !important; }
    table.zebra td { background-color: #fff !important; }
    table.zebra td:nth-child(2n) { background-color: #ccc !important; }

    /* ==========================================================================
    Helper classes
    ========================================================================== */

    .hidden {
    display: none !important;
    visibility: hidden;
    }

    /*
    * Hide visually and from screenreaders, but maintain layout
    */

    .invisible {
    visibility: hidden;
    }

    /*
    * Clearfix: contain floats
    *
    * For modern browsers
    * 1. The space content is one way to avoid an Opera bug when the
    * `contenteditable` attribute is included anywhere else in the document.
    * Otherwise it causes space to appear at the top and bottom of elements
    * that receive the `clearfix` class.
    * 2. The use of `table` rather than `block` is only necessary if using
    * `:before` to contain the top-margins of child elements.
    */

    .clearfix:before,
    .clearfix:after {
    content: " "; /* 1 */
    display: table; /* 2 */
    }

    .clearfix:after {
    clear: both;
    }

    /*
    * For IE 6/7 only
    * Include this rule to trigger hasLayout and contain floats.
    */

    .clearfix {
    *zoom: 1;
    }

    .ruler {
    border-left: 1px solid black;
    border-bottom: 1px solid black;
    border-right: 1px solid black;
    }
  2. swistak created this gist Jan 14, 2014.
    115 changes: 115 additions & 0 deletions pdf_generator.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,115 @@
    require 'fileutils'
    require 'logger'
    require 'erb'

    # Pdf generation service based on wkhtmltopdf.

    class PdfGenerator
    class WkhtmltopdfError < RuntimeError; end

    DPI = 96 # Pixels in inch
    DPC = (DPI / 2.54) # = 37.795 Pixels in centimeter

    # See http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf-0.9.9-doc.html
    #
    # Options without argument just take true, rest should have string as value.
    # To disable option set it to false.
    PDF_OPTIONS = {
    'quiet' => true,

    # Header is disabled.
    # 'header-html' => 'header.html',
    # 'footer-html' => 'footer.html',

    'print-media-type' => true,
    # 'disable-smart-shrinking' => true,

    'page-size' => 'A4',
    'margin-top' => '0.5cm',
    'margin-right' => '0.5cm',
    'margin-bottom' => '1cm',
    'margin-left' => '0.5cm',
    'encoding' => "UTF-8",
    }

    PDF_DIR = 'pdfs'
    FileUtils.mkdir_p(PDF_DIR)

    attr_accessor :error_message
    attr_accessor :note

    def logger
    defined?(Rails) ? Rails.logger : (@logger ||= Logger.new($stdout))
    end

    def pdf_options
    PDF_OPTIONS
    end

    def note_url
    'index.html'
    end

    def pdf_path
    File.join(PDF_DIR, "ramki.pdf")
    end

    def wkhtmltopdf
    @wkhtmltopdf ||= begin
    pdf_command = [
    `ls -1 bin/wkhtmltopdf* 2>/dev/null`.split("\n").last,
    `which wkhtmltopdf`
    ].detect{|c| !c.nil? }

    raise("Cannot locate wkhtmltopdf in #{Dir.pwd} or $PATH") if pdf_command.nil?

    pdf_command.strip
    end
    end

    def xvfb_wrapper
    "" #'xvfb-run -s "-screen 0, 1600x1200x24"'
    end

    # wkhtmltopdf [OPTIONS]... <input file> [More input files] <output file>
    def pdf_command
    normalized_options = pdf_options.map{|k, v|
    v ? "--#{k} #{v == true ? "" : v}" : nil
    }.compact.join(" ")

    "#{xvfb_wrapper} #{wkhtmltopdf} #{normalized_options} #{note_url} #{pdf_path}"
    end

    def render
    File.open('index.html', 'w') do |f|
    f.write ERB.new(File.read('index.html.erb')).result
    end
    end

    # Generates a pdf.
    def generate
    render
    perform
    end

    # Performs a pdf generation.
    def perform
    logger.debug "Running: " + pdf_command

    # See http://mentalized.net/journal/2010/03/08/5_ways_to_run_commands_from_ruby/
    redirection = " 2>&1"
    @out = IO.popen(pdf_command + redirection, "wb+") do |pdf|
    pdf.close_write
    pdf.gets(nil)
    end

    unless $? == 0
    self.error_message = "Failed to generate pdf via: #{wkhtmltopdf}. Exit code: #{$?}."
    raise(WkhtmltopdfError, error_message, caller[1..-1])
    end
    end
    end

    if __FILE__ == $0
    PdfGenerator.new.generate
    end