Skip to content

Instantly share code, notes, and snippets.

@coldnebo
Last active May 20, 2018 20:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save coldnebo/47e5edad33bdb3e1db78e8d1e66a29d3 to your computer and use it in GitHub Desktop.
Save coldnebo/47e5edad33bdb3e1db78e8d1e66a29d3 to your computer and use it in GitHub Desktop.
ruby orchestra preprint utils
declare -a arr=("violin1" "violin2" "viola" "cello" "bass")
for inst in "${arr[@]}"
do
#inst="violin2"
title="Souza - The Stars and Stripes Forever"
# create a blank letter page for titles
convert -background white -fill black -size 612x792 -pointsize 24 -gravity center label:"$inst\n$title" -page Letter ${inst}_00.ppm
pdfimages ${inst}.pdf -p ${inst}
convert "${inst}-*.ppm" -colorspace gray -normalize -crop 50%x100% -scene 1 +repage ${inst}_%02d.ppm
# here you have to manually decide whether the splits apply always.
#Otherwise, you have to convert single pages without the split in place of the others....
# like...
# convert ${inst}-001-000.ppm -colorspace gray -normalize ${inst}_01.ppm
convert ${inst}_*.ppm ${inst}.pdf
done
#!/usr/bin/env ruby
require 'pry'
require_relative 'preprint_utils'
QUANTITY = {
violin_1: 12,
violin_2: 14,
viola: 10,
cello: 12,
bass: 3
}
# basic features...
piece = PrePrint::Piece.new(
title: "Estampas Mexicanas",
composer: "José Luis Elizondo"
)
piece_dir = "scores/Elizondo_Estampas_Mexicanas"
piece.dsl do |p|
p.pdf_file = "#{piece_dir}/estampas-orchestra-i-ii-iii.pdf"
p.parts_dir = "#{piece_dir}"
p.extract_part( "flute_1", "62-63 112 138-140" )
p.extract_part( "flute_2", "64-65 113 141-143" )
p.extract_part( "piccolo", "60-61 111 135-137" )
p.extract_part( "oboe_1", "66-67 144-146" )
p.extract_part( "oboe_2", "68-69 147-149" )
p.extract_part( "clarinet_1", "70-71 114 150-152" )
p.extract_part( "clarinet_2", "72-73 115 153-155" )
p.extract_part( "bassoon", "74-75 156-158" )
p.extract_part( "horn_1", "76-77 116 159-161" )
p.extract_part( "horn_2", "78-79 117 162-164" )
p.extract_part( "trumpet_1", "80-81 165-167" )
p.extract_part( "trumpet_2", "82-83 168-170" )
p.extract_part( "trombone_1", "84-85 118 171-173" )
p.extract_part( "trombone_2", "86-87 119 174-176" )
p.extract_part( "timpani", "88-89 " )
p.extract_part( "percussion_combined", "90-94 120-123 177-184" )
p.extract_part( "percussion_1", "95-96 124 185-187" )
p.extract_part( "percussion_2", "97-98 125 188-190" )
p.extract_part( "percussion_3", "99-100 126 191-193" )
p.extract_part( "percussion_4", " 127 " )
p.extract_part( "violin_1", "101-102 128 194-196" ).quantity = QUANTITY[:violin_1]
p.extract_part( "violin_2", "103-104 129 197-199" ).quantity = QUANTITY[:violin_2]
p.extract_part( "viola", "105-106 130 200-202" ).quantity = QUANTITY[:viola]
p.extract_part( "cello_1", "107-108 131-132 203-205" ).quantity = QUANTITY[:cello]
p.extract_part( "cello_2", " 206-208" ).quantity = 6
p.extract_part( "bass", "109-110 133-134 209-211" ).quantity = QUANTITY[:bass]
end
piece.book_parts(book_dir: "#{piece_dir}/books")
piece.create_print_job(dir: "#{piece_dir}/print")
piece = PrePrint::Piece.new(
title: "Bolero",
composer: "Maurice Ravel"
)
piece_dir = "scores/Ravel - Bolero"
piece.dsl do |p|
p.parts_dir = "#{piece_dir}"
p.add_part( "violin_1", from_pdf: "violin1.pdf").quantity = QUANTITY[:violin_1]
p.add_part( "violin_2", from_pdf: "violin2.pdf").quantity = QUANTITY[:violin_2]
p.add_part( "viola", from_pdf: "viola.pdf" ).quantity = QUANTITY[:viola]
p.add_part( "cello", from_pdf: "cello.pdf" ).quantity = QUANTITY[:cello]
p.add_part( "bass", from_pdf: "bass.pdf" ).quantity = QUANTITY[:bass]
end
piece.book_parts(book_dir: "#{piece_dir}/books")
piece.create_print_job(dir: "#{piece_dir}/print")
piece = PrePrint::Piece.new(
title: "Symphonic Dances from West Side Story",
composer: "Leonard Bernstein"
)
piece_dir = "scores/Bernstein - Symphonic Dances from West Side Story"
piece.dsl do |p|
p.parts_dir = "#{piece_dir}"
p.add_part( "violin_1", from_pdf: "violin1.pdf").quantity = QUANTITY[:violin_1]
p.add_part( "violin_2", from_pdf: "violin2.pdf").quantity = QUANTITY[:violin_2]
p.add_part( "viola", from_pdf: "viola.pdf" ).quantity = QUANTITY[:viola]
p.add_part( "cello", from_pdf: "cello.pdf" ).quantity = QUANTITY[:cello]
p.add_part( "bass", from_pdf: "bass.pdf" ).quantity = QUANTITY[:bass]
end
piece.book_parts(book_dir: "#{piece_dir}/books")
piece.create_print_job(dir: "#{piece_dir}/print")
piece = PrePrint::Piece.new(
title: "Shenandoah",
composer: "Mack Wilberg"
)
piece_dir = "scores/Wilberg - Shenandoah"
piece.dsl do |p|
p.parts_dir = "#{piece_dir}"
p.add_part( "violin_1", from_pdf: "violin1.pdf").quantity = QUANTITY[:violin_1]
p.add_part( "violin_2", from_pdf: "violin2.pdf").quantity = QUANTITY[:violin_2]
p.add_part( "viola", from_pdf: "viola.pdf" ).quantity = QUANTITY[:viola]
p.add_part( "cello", from_pdf: "cello.pdf" ).quantity = QUANTITY[:cello]
p.add_part( "bass", from_pdf: "bass.pdf" ).quantity = QUANTITY[:bass]
end
piece.book_parts(book_dir: "#{piece_dir}/books")
piece.create_print_job(dir: "#{piece_dir}/print")
piece = PrePrint::Piece.new(
title: "The Stars and Stripes Forever",
composer: "John Philip Sousa"
)
piece_dir = "scores/Sousa - The Stars and Stripes Forever"
piece.dsl do |p|
p.parts_dir = "#{piece_dir}"
p.add_part( "violin_1", from_pdf: "violin1.pdf").quantity = QUANTITY[:violin_1]
p.add_part( "violin_2", from_pdf: "violin2.pdf").quantity = QUANTITY[:violin_2]
p.add_part( "viola", from_pdf: "viola.pdf" ).quantity = QUANTITY[:viola]
p.add_part( "cello", from_pdf: "cello.pdf" ).quantity = QUANTITY[:cello]
p.add_part( "bass", from_pdf: "bass.pdf" ).quantity = QUANTITY[:bass]
end
piece.book_parts(book_dir: "#{piece_dir}/books")
piece.create_print_job(dir: "#{piece_dir}/print")
require 'fileutils'
require 'hashie'
def cmd(str)
puts "CMD: #{str}"
`#{str}`
end
def pdf_info(file)
info = `pdfinfo "#{file}"`
info = info.split("\n").map{|line| line.match(/^(?<key>[^:]*):\s+(?<value>.*)/).captures rescue nil }.compact
info = Hashie::Mash.new(Hash[info])
info.pages = Integer(info.Pages)
info.file_size = Integer(info["File size"].match(/(\d+) bytes/)[1])
info
rescue Exception => e
binding.pry
end
module PrePrint
class Part
attr_accessor :pdf_file, :name, :quantity, :book_file, :book_file_size
def initialize(name:, pdf_file:)
@name = name
@pdf_file = pdf_file
@quantity = 1
end
end
class Piece
attr_accessor :title, :composer, :parts, :pdf_file, :parts_dir
def initialize(title:, composer:)
@title = title
@composer = composer
@parts = {}
end
def short_name
"#{composer.split.last}-#{title.split.join('_')}"
end
def part_path(file)
File.join(parts_dir, file)
end
# provides access to methods as a script.
def dsl
yield(self)
end
# @param name String name of the part
# @param ranges String page ranges to extract, e.g. "1-4 5 9-10"
def extract_part(name, ranges, from_pdf: nil)
raise ArgumentError, "parts_dir must be set first!" if parts_dir.nil?
from_pdf = unless from_pdf.nil?
part_path(from_pdf)
else
pdf_file
end
FileUtils.mkdir(parts_dir) unless File.exists?(parts_dir)
part_filename = "#{short_name}-#{name}.pdf"
self.parts[name] = Part.new(name: name, pdf_file: part_path(part_filename))
unless File.exists?(part_path(part_filename))
cmd %{pdftk "#{from_pdf}" cat #{ranges} output "#{part_path(part_filename)}"}
else
puts "part #{part_path(part_filename)} already exists, leaving alone."
end
self.parts[name]
end
def add_part(name, from_pdf:)
unless File.exists?(part_path(from_pdf))
raise "part file #{part_path(from_pdf)} doesn't exist!"
end
self.parts[name] = Part.new(name: name, pdf_file: part_path(from_pdf))
end
# take all parts and turn them into books (11x17)
def book_parts(book_dir:)
unless File.exists?(book_dir)
FileUtils.mkdir(book_dir)
end
parts.each do |name, part|
part.book_file = File.join(book_dir, File.basename(part.pdf_file))
unless File.exists?(part.book_file)
pages = pdf_info(part.pdf_file).pages
# if pages == 1
# cmd %{pdfjam --no-landscape --paper 'letter' --outfile "#{part.book_file}" -- "#{part.pdf_file}"}
# elsif pages < 3
# cmd %{pdfnup --nup 2x1 --landscape --papersize '{11in,17in}' --outfile "#{part.book_file}" -- "#{part.pdf_file}"}
# else
#cmd %{pdfbook --short-edge --twoside --papersize '{11in,17in}' --outfile "#{part.book_file}" -- "#{part.pdf_file}"}
i = 300
#cmd %{convert -resize "792x1224" -gravity center -background white -extent "792x1224" -compress jpeg -quality 70 "#{part.pdf_file}" "#{part.pdf_file}.conv.pdf"}
cmd %{convert -density "#{i}x#{i}" -units PixelsPerInch -resize "#{i*8.5}x#{i*11}" -repage "#{i*8.5}x#{i*11}" -compress jpeg -quality 85 "#{part.pdf_file}" "#{part.pdf_file}.conv.pdf"}
cmd %{pdfbook --short-edge --twoside --papersize '{11in,17in}' --noautoscale false --outfile "#{part.book_file}" -- "#{part.pdf_file}.conv.pdf"}
cmd %{rm "#{part.pdf_file}.conv.pdf"}
# end
else
puts "book part #{part.book_file} already exists, leaving alone."
end
info = pdf_info(part.book_file)
part.book_file_size = info.file_size
end
end
# kinkos allows 150MB max, but says 100MB max, let's do 90 MB
CHUNK_MAX = 90 * (1024 ** 2)
def create_print_job(dir:)
unless File.exists?(dir)
FileUtils.mkdir(dir)
else
puts "print job dir already exists. skipping."
return
end
chunk = 1
chunk_size = 0
chunk_name = "#{short_name}-chunk-#{chunk}.pdf"
parts.each do |name, part|
part.quantity.times do
if chunk_size + part.book_file_size > CHUNK_MAX
# start a new chunk
chunk += 1
chunk_size = 0
chunk_name = "#{short_name}-chunk-#{chunk}.pdf"
end
# incr the size of the current chunk
chunk_size = chunk_size + part.book_file_size
chunk_file = File.join(dir, chunk_name)
unless File.exists?(chunk_file)
cmd %{cp "#{part.book_file}" "#{chunk_file}"}
else
# append part to end of chunk
cmd %{pdftk "#{chunk_file}" "#{part.book_file}" cat output "#{chunk_file}.tmp"}
cmd %{rm "#{chunk_file}"}
cmd %{mv "#{chunk_file}.tmp" "#{chunk_file}"}
end
end
end
end
end
end
@coldnebo
Copy link
Author

Incoming scans may have to be split and then resized into single pages. if there is a page turn, then a title sheet must be added. This is in im_manip_scripts.sh and changes every job depending on what is needed... very manual.

Once the job is normalized, then the files can be put into the standard structure and run on the preprint.rb dsl. The dsl allows for various transforms and pipelines: 1) sometimes scores come in a single pdf, and must be split by page numbers, 2) sometimes parts are individual pdfs, 3) sometimes parts are scans and must be normalized for grey level, size and paper size.

At the end, output is separated into 'print' (which contains the booklet layouts for each part) and 'books' which contain the final concatenated print job optimized for Kinko's online printing. There are size limits, so these are chunked. (usually size only comes into play for scanned pdfs). The parts are assembled in bulk because submitting them separately would be too time consuming in many cases. However, bulk processing has other disadvantages, namely that the parts come back in random order with the bifold, so one must separate, sort and then reassemble the parts to ensure proper printing.

Currently, this process is ramshackle. I have tried to automate it further, but differences in size, scan quality, print transforms (there is an annoying artifact where the booklets are blurrier than the originals, but I can't determine exactly where this happens in the process... there is a lot of room for improvement. This is perhaps adequate for rehearsal scores, barely, but maybe these scripts can help the next person if they want to build on the process steps or the ruby themselves.

Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment