Last active
May 20, 2018 20:26
-
-
Save coldnebo/47e5edad33bdb3e1db78e8d1e66a29d3 to your computer and use it in GitHub Desktop.
ruby orchestra preprint utils
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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") | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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!