From cb6737a6170afea4d330387369965c921c375ee1 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sat, 23 Feb 2013 14:21:44 -0600
Subject: [PATCH 01/54] Flip RbConfig conditional around the correct direction.
(Config currently still resolves in 1.9 w/ a warning)
---
lib/docsplit/pdf_extractor.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index 5f6afc0..391a6e9 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -4,7 +4,7 @@ module Docsplit
class PdfExtractor
@@executable = nil
- HOST_OS = (defined?("Config") ? Config : RbConfig)::CONFIG['host_os']
+ HOST_OS = (defined?("RbConfig") ? RbConfig : Config)::CONFIG['host_os']
def windows?
!!HOST_OS.match(/mswin|windows|cygwin/i)
end
From abf4d8014e065e1a9235aad0def574d143bdf2e9 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sat, 23 Feb 2013 14:21:54 -0600
Subject: [PATCH 02/54] Add performance note.
---
index.html | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/index.html b/index.html
index 97aaf16..3f7e532 100755
--- a/index.html
+++ b/index.html
@@ -285,7 +285,8 @@ Change Log
– Feb. 21, 2013
Docsplit now expresses a preference for LibreOffice over OpenOffice, with
- an eye to removing JODConverter and OpenOffice support in future versions.
+ an eye to removing JODConverter and OpenOffice support in future versions
+ (direct LibreOffice support is substantially faster than JODConverter).
Improved unicode support now correctly collects non-ascii characters from
pdfinfo.
From dab8bf9434a879eab759d9832cd93473cb43af29 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sat, 23 Feb 2013 16:11:04 -0600
Subject: [PATCH 03/54] Revert change meant to help in multi-user environments.
---
lib/docsplit/transparent_pdfs.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/docsplit/transparent_pdfs.rb b/lib/docsplit/transparent_pdfs.rb
index f65072d..cd0969f 100755
--- a/lib/docsplit/transparent_pdfs.rb
+++ b/lib/docsplit/transparent_pdfs.rb
@@ -12,7 +12,7 @@ def ensure_pdfs(docs)
if ext.downcase == '.pdf'
doc
else
- tempdir = Dir.mktmpdir
+ tempdir = File.join(Dir.tmpdir, 'docsplit')
extract_pdf([doc], {:output => tempdir})
File.join(tempdir, File.basename(doc, ext) + '.pdf')
end
From be811fa301bf8390fff59bead433486f28e9fa0f Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sat, 23 Feb 2013 16:30:33 -0600
Subject: [PATCH 04/54] Gem bumping.
---
docsplit.gemspec | 2 +-
index.html | 4 ++--
lib/docsplit.rb | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/docsplit.gemspec b/docsplit.gemspec
index f5abcc8..7cb7a11 100755
--- a/docsplit.gemspec
+++ b/docsplit.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'docsplit'
- s.version = '0.7.0' # Keep version in sync with docsplit.rb
+ s.version = '0.7.1' # Keep version in sync with docsplit.rb
s.date = '2013-02-21'
s.homepage = "http://documentcloud.github.com/docsplit/"
diff --git a/index.html b/index.html
index 3f7e532..74af23a 100755
--- a/index.html
+++ b/index.html
@@ -98,7 +98,7 @@ Doc⚡split
(title, author, number of pages...)
- Docsplit is currently at version 0.7.0.
+ Docsplit is currently at version 0.7.1.
Docsplit is an open-source component of DocumentCloud.
@@ -283,7 +283,7 @@
Internals
Change Log
- – Feb. 21, 2013
+ – Feb. 21, 2013
Docsplit now expresses a preference for LibreOffice over OpenOffice, with
an eye to removing JODConverter and OpenOffice support in future versions
(direct LibreOffice support is substantially faster than JODConverter).
diff --git a/lib/docsplit.rb b/lib/docsplit.rb
index c05b5a0..59288eb 100755
--- a/lib/docsplit.rb
+++ b/lib/docsplit.rb
@@ -5,7 +5,7 @@
# The Docsplit module delegates to the Java PDF extractors.
module Docsplit
- VERSION = '0.7.0' # Keep in sync with gemspec.
+ VERSION = '0.7.1' # Keep in sync with gemspec.
ESCAPE = lambda {|x| Shellwords.shellescape(x) }
From 30c357f8b62151f1724b1c67ec6151b7f0873bc4 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sat, 23 Feb 2013 22:45:19 -0600
Subject: [PATCH 05/54] Add config for user profile.
---
lib/docsplit/pdf_extractor.rb | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index 391a6e9..03c1c9e 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -95,12 +95,15 @@ def extract(docs, opts)
`gm convert #{escaped_doc} #{escaped_out}/#{escaped_basename}.pdf`
else
if libre_office?
- options = "--headless --convert-to pdf --outdir #{escaped_out} #{escaped_doc}"
+ # Set the LibreOffice user profile, so
+ ENV['SYSUSERCONFIG']="file://#{File.expand_path(escaped_out)}"
+
+ options = "--headless --invisible --norestore --nolockcheck --convert-to pdf --outdir #{escaped_out} #{escaped_doc}"
cmd = "#{office_executable} #{options} 2>&1"
result = `#{cmd}`.chomp
raise ExtractionFailed, result if $? != 0
true
- else # open office presumably
+ else # open office presumably, rely on JODConverter to figure it out.
options = "-jar #{ESCAPED_ROOT}/vendor/jodconverter/jodconverter-core-3.0-beta-4.jar -r #{ESCAPED_ROOT}/vendor/conf/document-formats.js"
run_jod "#{options} #{escaped_doc} #{escaped_out}/#{escaped_basename}.pdf", [], {}
end
From 14f85bbec8cb81aa23819ae28b0ba2babc41ba27 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sat, 23 Feb 2013 22:45:33 -0600
Subject: [PATCH 06/54] Commenting
---
lib/docsplit/pdf_extractor.rb | 23 +++++++++++++++++++----
1 file changed, 19 insertions(+), 4 deletions(-)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index 03c1c9e..bac26f6 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -4,6 +4,7 @@ module Docsplit
class PdfExtractor
@@executable = nil
+ # Provide a set of helper functions to determine the OS.
HOST_OS = (defined?("RbConfig") ? RbConfig : Config)::CONFIG['host_os']
def windows?
!!HOST_OS.match(/mswin|windows|cygwin/i)
@@ -15,18 +16,23 @@ def linux?
!!HOST_OS.match(/linux/i)
end
+ # The first line of the help output holds the name and version number
+ # of the office software to be used for extraction.
def version_string
@@help ||= `#{office_executable} -h 2>&1`.split("\n").first
end
-
def libre_office?
!!version_string.match(/^LibreOffice/)
end
-
def open_office?
!!version_string.match(/^OpenOffice.org/)
end
+ # A set of default locations to search for office software
+ # These have been extracted from JODConverter. Each listed
+ # path should contain a directory "program" which in turn
+ # contains the "soffice" executable.
+ # see: https://github.com/mirkonasato/jodconverter/blob/master/jodconverter-core/src/main/java/org/artofsolving/jodconverter/office/OfficeUtils.java#L63-L91
def office_search_paths
if windows?
office_names = ["LibreOffice 3", "LibreOffice 4", "OpenOffice.org 3"]
@@ -48,14 +54,19 @@ def office_search_paths
search_paths
end
+ # Identify the path to a working office executable.
def office_executable
paths = office_search_paths
+ # If an OFFICE_PATH has been specified on the commandline
+ # raise an error if that path isn't valid, otherwise, add
+ # it to the front of our search paths.
if ENV['OFFICE_PATH']
raise ArgumentError, "No such file or directory #{ENV['OFFICE_PATH']}" unless File.exists? ENV['OFFICE_PATH']
paths.unshift(ENV['OFFICE_PATH'])
end
+ # The location of the office executable is OS dependent
path_pieces = ["soffice"]
if windows?
path_pieces += [["program", "soffice.bin"]]
@@ -65,6 +76,8 @@ def office_executable
path_pieces += [["program", "soffice"]]
end
+ # Search for the first suitable office executable
+ # and short circuit an executable is found.
paths.each do |path|
if File.exists? path
@@executable ||= path unless File.directory? path
@@ -79,10 +92,12 @@ def office_executable
@@executable
end
+ # Used to specify the office location for JODConverter
def office_path
File.dirname(File.dirname(office_executable))
end
+ # Convert documents to PDF.
def extract(docs, opts)
out = opts[:output] || '.'
FileUtils.mkdir_p out unless File.exists?(out)
@@ -95,9 +110,9 @@ def extract(docs, opts)
`gm convert #{escaped_doc} #{escaped_out}/#{escaped_basename}.pdf`
else
if libre_office?
- # Set the LibreOffice user profile, so
+ # Set the LibreOffice user profile, so that parallel uses of cloudcrowd don't trip over each other.
ENV['SYSUSERCONFIG']="file://#{File.expand_path(escaped_out)}"
-
+
options = "--headless --invisible --norestore --nolockcheck --convert-to pdf --outdir #{escaped_out} #{escaped_doc}"
cmd = "#{office_executable} #{options} 2>&1"
result = `#{cmd}`.chomp
From 00d8e916a2ccdf3a99cce9f3ebba0e7cb081025a Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sun, 24 Feb 2013 00:49:24 -0600
Subject: [PATCH 07/54] Updating for 0.7.2
---
docsplit.gemspec | 2 +-
index.html | 9 +++++++--
lib/docsplit.rb | 2 +-
3 files changed, 9 insertions(+), 4 deletions(-)
diff --git a/docsplit.gemspec b/docsplit.gemspec
index 7cb7a11..d3526bb 100755
--- a/docsplit.gemspec
+++ b/docsplit.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'docsplit'
- s.version = '0.7.1' # Keep version in sync with docsplit.rb
+ s.version = '0.7.2' # Keep version in sync with docsplit.rb
s.date = '2013-02-21'
s.homepage = "http://documentcloud.github.com/docsplit/"
diff --git a/index.html b/index.html
index 74af23a..981c34d 100755
--- a/index.html
+++ b/index.html
@@ -98,7 +98,7 @@ Doc⚡split
(title, author, number of pages...)
- Docsplit is currently at version 0.7.1.
+ Docsplit is currently at version 0.7.2.
Docsplit is an open-source component of DocumentCloud.
@@ -283,7 +283,12 @@
Internals
Change Log
- – Feb. 21, 2013
+ – Feb. 23, 2013
+ Bug fixes for LibreOffice support.
+
+
+
+ – Feb. 23, 2013
Docsplit now expresses a preference for LibreOffice over OpenOffice, with
an eye to removing JODConverter and OpenOffice support in future versions
(direct LibreOffice support is substantially faster than JODConverter).
diff --git a/lib/docsplit.rb b/lib/docsplit.rb
index 59288eb..5001413 100755
--- a/lib/docsplit.rb
+++ b/lib/docsplit.rb
@@ -5,7 +5,7 @@
# The Docsplit module delegates to the Java PDF extractors.
module Docsplit
- VERSION = '0.7.1' # Keep in sync with gemspec.
+ VERSION = '0.7.2' # Keep in sync with gemspec.
ESCAPE = lambda {|x| Shellwords.shellescape(x) }
From 79b75cdc639a724fa611e8a37423bad56adba05a Mon Sep 17 00:00:00 2001
From: Senner
Date: Tue, 12 Mar 2013 13:58:21 -0500
Subject: [PATCH 08/54] new libreoffice has --version
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
this should match if first line didn't have a version number i.e. error like: Error: Could not find or load main class .usr.lib.libreoffice or Failed to open display which shows if running headless.
---
lib/docsplit/pdf_extractor.rb | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index bac26f6..1e229cc 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -19,7 +19,12 @@ def linux?
# The first line of the help output holds the name and version number
# of the office software to be used for extraction.
def version_string
- @@help ||= `#{office_executable} -h 2>&1`.split("\n").first
+ versionstr = `#{office_executable} -h 2>&1`.split("\n").first
+ if !!versionstr.match(/[0-9]*/)
+ versionstr = `#{office_executable} --version`.split("\n").first
+ end
+ @@help ||= versionstr
+
end
def libre_office?
!!version_string.match(/^LibreOffice/)
From 5cca3df373ae1e4fdae9db4906aee0fea12f92ad Mon Sep 17 00:00:00 2001
From: Fedor Sumkin
Date: Sat, 13 Apr 2013 02:08:19 +0400
Subject: [PATCH 09/54] typo fix for win
---
lib/docsplit/pdf_extractor.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index bac26f6..eeb790d 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -37,7 +37,7 @@ def office_search_paths
if windows?
office_names = ["LibreOffice 3", "LibreOffice 4", "OpenOffice.org 3"]
program_files_path = ENV["CommonProgramFiles"]
- search_paths = office_name.map{ |program| File.join(program_files_path, program) }
+ search_paths = office_names.map{ |program| File.join(program_files_path, program) }
elsif osx?
search_paths = %w(
/Applications/LibreOffice.app/Contents
From a6b20fdbae2b116efdca6ab7ba1000124d67746e Mon Sep 17 00:00:00 2001
From: Andrew Frankel
Date: Wed, 29 May 2013 11:42:24 -0400
Subject: [PATCH 10/54] Use String#encode in text_cleaner
---
lib/docsplit/text_cleaner.rb | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/lib/docsplit/text_cleaner.rb b/lib/docsplit/text_cleaner.rb
index 123f74a..c4aac01 100644
--- a/lib/docsplit/text_cleaner.rb
+++ b/lib/docsplit/text_cleaner.rb
@@ -35,8 +35,13 @@ class TextCleaner
# For the time being, `clean` uses the regular StringScanner, and not the
# multibyte-aware version, coercing to ASCII first.
def clean(text)
- require 'iconv' unless defined?(Iconv)
- text = Iconv.iconv('ascii//translit//ignore', 'utf-8', text).first
+ if String.method_defined?(:encode)
+ text.encode!('ascii', :invalid => :replace, :undef => :replace, :replace => '?')
+ else
+ require 'iconv' unless defined?(Iconv)
+ text = Iconv.iconv('ascii//translit//ignore', 'utf-8', text).first
+ end
+
scanner = StringScanner.new(text)
cleaned = []
spaced = false
From 7f2591fe0f77a06643b8d1fc49ade343a09b1793 Mon Sep 17 00:00:00 2001
From: Elia Schito
Date: Wed, 24 Jul 2013 14:24:34 +0200
Subject: [PATCH 11/54] Add /usr/lib64 to office_search_paths
As it's used by Oracle RHEL 6.3 and probably by RHEL in general.
---
lib/docsplit/pdf_extractor.rb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index eeb790d..10fd5fc 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -46,8 +46,10 @@ def office_search_paths
else # probably linux/unix
search_paths = %w(
/usr/lib/libreoffice
+ /usr/lib64/libreoffice
/opt/libreoffice
/usr/lib/openoffice
+ /usr/lib64/openoffice
/opt/openoffice.org3
)
end
From 64eb2b2c8d14e2fd221ac85e4254c367decdba49 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Fri, 26 Apr 2013 00:39:11 -0500
Subject: [PATCH 12/54] Updating License file
---
LICENSE | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/LICENSE b/LICENSE
index 38e96bf..76383ba 100755
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,7 @@
JODConverter ius licensed under the LGPL: gnu.org/licenses/lgpl.html
-Copyright (c) 2009 Jeremy Ashkenas, DocumentCloud
+Copyright (c) 2009-2011 Jeremy Ashkenas, DocumentCloud
+Copyright (c) 2011-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
From 16fc6a91b6d861cf6e5bf2d2e2beb6628b5b0151 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Fri, 26 Apr 2013 00:39:37 -0500
Subject: [PATCH 13/54] For the time being, setting language should turn off
cleaning.
---
lib/docsplit/command_line.rb | 1 +
1 file changed, 1 insertion(+)
diff --git a/lib/docsplit/command_line.rb b/lib/docsplit/command_line.rb
index 7c7af08..60ee7ef 100755
--- a/lib/docsplit/command_line.rb
+++ b/lib/docsplit/command_line.rb
@@ -96,6 +96,7 @@ def parse_options
end
opts.on('-l', '--language [LANGUAGE]', 'set the language (ISO 639-2/T code) for text extraction') do |l|
@options[:language] = l
+ @options[:clean] = false
end
opts.on('-r', '--rolling', 'generate images from each previous image') do |r|
@options[:rolling] = true
From 8086ad30b8014d6c5a81cd3299d85688a2edc100 Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sun, 19 Jan 2014 10:03:54 -0600
Subject: [PATCH 14/54] If UTF-8 encoding isn't valid for InfoExtractor, force
the encoding.
---
lib/docsplit/info_extractor.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/docsplit/info_extractor.rb b/lib/docsplit/info_extractor.rb
index ce03626..2e34c85 100644
--- a/lib/docsplit/info_extractor.rb
+++ b/lib/docsplit/info_extractor.rb
@@ -27,7 +27,7 @@ def extract_all(pdfs, opts)
raise ExtractionFailed, result if $? != 0
# ruby 1.8 (iconv) and 1.9 (String#encode) :
if String.method_defined?(:encode)
- result.encode!('UTF-8', 'UTF-8', :invalid => :replace)
+ result.encode!('UTF-8', 'binary', :invalid => :replace, :undef => :replace, :replace => "") unless result.valid_encoding?
else
require 'iconv' unless defined?(Iconv)
ic = Iconv.new('UTF-8//IGNORE','UTF-8')
From 8e484efe2ded60a0c125356b172b5fac5310f0bc Mon Sep 17 00:00:00 2001
From: Nathan Stitt
Date: Thu, 30 Jan 2014 13:04:01 -0600
Subject: [PATCH 15/54] Redirect STDERR from help to /dev/null
On libreoffice > 4, the -h flag attempts to open the UI to display the
help, in addition to writing it to stdout.
This means that on Linux without X11 running, the output ends up looking like:
Failed to open display
LibreOffice 4.1.1.2 410m0(Build:2)
This behavior can be avoided by using the `--headless` flag, but that
flag isn't supported in older version of OpenOffice, causing an error
there.
Since Docsplit only looks at the first line of output it doesn't find
the version string, causing it to assume it should use OpenOffice in the
extract method. It then attempts to use the JOD convertor, which will
fail on recent versions of LibreOffice.
I believe this is the root cause of issue #71 and should correct it.
---
lib/docsplit/pdf_extractor.rb | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index bbe65f1..087e792 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -19,11 +19,12 @@ def linux?
# The first line of the help output holds the name and version number
# of the office software to be used for extraction.
def version_string
- versionstr = `#{office_executable} -h 2>&1`.split("\n").first
- if !!versionstr.match(/[0-9]*/)
- versionstr = `#{office_executable} --version`.split("\n").first
- end
- @@help ||= versionstr
+ null = windows? ? "NUL" : "/dev/null"
+ versionstr = `#{office_executable} -h 2>#{null}`.split("\n").first
+ if !!versionstr.match(/[0-9]*/)
+ versionstr = `#{office_executable} --version`.split("\n").first
+ end
+ @@help ||= versionstr
end
def libre_office?
From 1b6a6cdfd86d90d5175b5476a2d445a1985c25ee Mon Sep 17 00:00:00 2001
From: Nathan Stitt
Date: Thu, 30 Jan 2014 13:36:07 -0600
Subject: [PATCH 16/54] Fix the PDFExtractor.version_string's memoization
Looked like there was an attempt to memoize the version_string
in the @@help class var but the cached var was only consulted after
the system call had already been executed.
This caches the results in the @@version_string var.
---
lib/docsplit/pdf_extractor.rb | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index 087e792..99b5081 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -2,7 +2,8 @@
module Docsplit
class PdfExtractor
- @@executable = nil
+ @@executable = nil
+ @@version_string = nil
# Provide a set of helper functions to determine the OS.
HOST_OS = (defined?("RbConfig") ? RbConfig : Config)::CONFIG['host_os']
@@ -19,13 +20,14 @@ def linux?
# The first line of the help output holds the name and version number
# of the office software to be used for extraction.
def version_string
- null = windows? ? "NUL" : "/dev/null"
- versionstr = `#{office_executable} -h 2>#{null}`.split("\n").first
- if !!versionstr.match(/[0-9]*/)
- versionstr = `#{office_executable} --version`.split("\n").first
+ unless @@version_string
+ null = windows? ? "NUL" : "/dev/null"
+ @@version_string = `#{office_executable} -h 2>#{null}`.split("\n").first
+ if !!@@version_string.match(/[0-9]*/)
+ @@version_string = `#{office_executable} --version`.split("\n").first
+ end
end
- @@help ||= versionstr
-
+ @@version_string
end
def libre_office?
!!version_string.match(/^LibreOffice/)
From 57c9369837e89d9e3115f15b98ab30ddb135c46a Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sun, 16 Feb 2014 12:07:55 -0600
Subject: [PATCH 17/54] Update gemspec.
---
docsplit.gemspec | 5 +++--
lib/docsplit.rb | 2 +-
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/docsplit.gemspec b/docsplit.gemspec
index d3526bb..8d0b9b0 100755
--- a/docsplit.gemspec
+++ b/docsplit.gemspec
@@ -1,7 +1,7 @@
Gem::Specification.new do |s|
s.name = 'docsplit'
- s.version = '0.7.2' # Keep version in sync with docsplit.rb
- s.date = '2013-02-21'
+ s.version = '0.7.3' # Keep version in sync with docsplit.rb
+ s.date = '2014-02-16'
s.homepage = "http://documentcloud.github.com/docsplit/"
s.summary = "Break Apart Documents into Images, Text, Pages and PDFs"
@@ -15,6 +15,7 @@ Gem::Specification.new do |s|
s.authors = ['Jeremy Ashkenas', 'Samuel Clay', 'Ted Han']
s.email = 'opensource@documentcloud.org'
s.rubyforge_project = 'docsplit'
+ s.license = 'MIT'
s.require_paths = ['lib']
s.executables = ['docsplit']
diff --git a/lib/docsplit.rb b/lib/docsplit.rb
index 5001413..58366d0 100755
--- a/lib/docsplit.rb
+++ b/lib/docsplit.rb
@@ -5,7 +5,7 @@
# The Docsplit module delegates to the Java PDF extractors.
module Docsplit
- VERSION = '0.7.2' # Keep in sync with gemspec.
+ VERSION = '0.7.3' # Keep in sync with gemspec.
ESCAPE = lambda {|x| Shellwords.shellescape(x) }
From 9172e309bca6f26cb3091221b487a7f85c1a755f Mon Sep 17 00:00:00 2001
From: Ted Han
Date: Sun, 16 Feb 2014 12:10:22 -0600
Subject: [PATCH 18/54] Missed a merge, updating gemspec and pushing gem again.
---
docsplit.gemspec | 2 +-
lib/docsplit.rb | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/docsplit.gemspec b/docsplit.gemspec
index 8d0b9b0..97b06de 100755
--- a/docsplit.gemspec
+++ b/docsplit.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'docsplit'
- s.version = '0.7.3' # Keep version in sync with docsplit.rb
+ s.version = '0.7.4' # Keep version in sync with docsplit.rb
s.date = '2014-02-16'
s.homepage = "http://documentcloud.github.com/docsplit/"
diff --git a/lib/docsplit.rb b/lib/docsplit.rb
index 58366d0..8d0d41b 100755
--- a/lib/docsplit.rb
+++ b/lib/docsplit.rb
@@ -5,7 +5,7 @@
# The Docsplit module delegates to the Java PDF extractors.
module Docsplit
- VERSION = '0.7.3' # Keep in sync with gemspec.
+ VERSION = '0.7.4' # Keep in sync with gemspec.
ESCAPE = lambda {|x| Shellwords.shellescape(x) }
From 929a42638999aba5e11883c1a5adad9436f03223 Mon Sep 17 00:00:00 2001
From: "Tobias L. Maier"
Date: Wed, 19 Feb 2014 14:45:18 +0100
Subject: [PATCH 19/54] Check if file is PDF by magic number. Closes #98
See http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/pdf_reference_1-7.pdf page 92
---
lib/docsplit/transparent_pdfs.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/docsplit/transparent_pdfs.rb b/lib/docsplit/transparent_pdfs.rb
index cd0969f..e5ef82f 100755
--- a/lib/docsplit/transparent_pdfs.rb
+++ b/lib/docsplit/transparent_pdfs.rb
@@ -9,7 +9,7 @@ module TransparentPDFs
def ensure_pdfs(docs)
[docs].flatten.map do |doc|
ext = File.extname(doc)
- if ext.downcase == '.pdf'
+ if ext.downcase == '.pdf' || File.open(doc, &:readline) =~ /\A\%PDF-\d+(\.\d+)?$/
doc
else
tempdir = File.join(Dir.tmpdir, 'docsplit')
@@ -23,4 +23,4 @@ def ensure_pdfs(docs)
extend TransparentPDFs
-end
\ No newline at end of file
+end
From 4f0ba570cc57daf56964c9f485ae40412a7c96d3 Mon Sep 17 00:00:00 2001
From: Serene Yew
Date: Tue, 25 Feb 2014 01:00:00 -0700
Subject: [PATCH 20/54] Add office search path to check vendor folder for use
with Heroku and libreoffice build pack
---
lib/docsplit/pdf_extractor.rb | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/docsplit/pdf_extractor.rb b/lib/docsplit/pdf_extractor.rb
index 99b5081..12069c7 100644
--- a/lib/docsplit/pdf_extractor.rb
+++ b/lib/docsplit/pdf_extractor.rb
@@ -52,6 +52,7 @@ def office_search_paths
/Applications/OpenOffice.org.app/Contents
)
else # probably linux/unix
+ # heroku libreoffice buildpack: https://github.com/rishihahs/heroku-buildpack-libreoffice
search_paths = %w(
/usr/lib/libreoffice
/usr/lib64/libreoffice
@@ -59,6 +60,7 @@ def office_search_paths
/usr/lib/openoffice
/usr/lib64/openoffice
/opt/openoffice.org3
+ /app/vendor/libreoffice
)
end
search_paths
From 4c490c9b0331348fcf4bd52a4107c3cc2a7bc9d7 Mon Sep 17 00:00:00 2001
From: jonoterc
Date: Mon, 24 Mar 2014 15:45:28 -0400
Subject: [PATCH 21/54] making magic number-based detection of PDFs
encoding-friendly, with tests
Magic number-based PDF-detection is vulnerable to encoding issues; forcing the first line to be read in binary mode should work across encodings (as the "%PDF" marker will be present using ASCII-7 characters in any case). Also, removing the "end of line" anchor from the PDF version regex to avoid issues with non-printing characters in certain cases.
Added tests, details:
- extracted PDF detection from ensure_pdfs method for more direct testing
- new unit tests verify that PDFs without the ".pdf" extensions are detected as PDFs
- samples are included under "test/fixtures/without_pdf_extension"
- add further PDFs (without extensions) and they will be automatically included in tests
- PDFs generated by Adobe InDesign previously raised encoding errors; samples (for each recent PDF version) are included
- new unit tests verify that files with the ".pdf" extensions are always detected as PDFs (regardless of file contents)
- samples are included under "test/fixtures/with_pdf_extension"
- samples file names reflect actual contents
- not sure that this is the correct behavior, but not looking to change current approach
---
lib/docsplit/transparent_pdfs.rb | 9 ++++--
.../with_pdf_extension/actually_a_doc.pdf | Bin 0 -> 52224 bytes
.../with_pdf_extension/actually_an_image.pdf | Bin 0 -> 34964 bytes
.../with_pdf_extension/actually_an_rtf.pdf | Bin 0 -> 7796 bytes
.../this_ones_a_real_pdf.pdf | Bin 0 -> 66118 bytes
.../indesign/test_pdf_1_3 | Bin 0 -> 8349 bytes
.../indesign/test_pdf_1_4 | Bin 0 -> 8350 bytes
.../indesign/test_pdf_1_5 | Bin 0 -> 8349 bytes
.../indesign/test_pdf_1_6 | Bin 0 -> 8349 bytes
.../indesign/test_pdf_1_7 | Bin 0 -> 8349 bytes
test/unit/test_transparent_pdfs.rb | 29 ++++++++++++++++++
11 files changed, 35 insertions(+), 3 deletions(-)
create mode 100755 test/fixtures/with_pdf_extension/actually_a_doc.pdf
create mode 100644 test/fixtures/with_pdf_extension/actually_an_image.pdf
create mode 100755 test/fixtures/with_pdf_extension/actually_an_rtf.pdf
create mode 100644 test/fixtures/with_pdf_extension/this_ones_a_real_pdf.pdf
create mode 100644 test/fixtures/without_pdf_extension/indesign/test_pdf_1_3
create mode 100644 test/fixtures/without_pdf_extension/indesign/test_pdf_1_4
create mode 100644 test/fixtures/without_pdf_extension/indesign/test_pdf_1_5
create mode 100644 test/fixtures/without_pdf_extension/indesign/test_pdf_1_6
create mode 100644 test/fixtures/without_pdf_extension/indesign/test_pdf_1_7
create mode 100644 test/unit/test_transparent_pdfs.rb
diff --git a/lib/docsplit/transparent_pdfs.rb b/lib/docsplit/transparent_pdfs.rb
index e5ef82f..8987b3b 100755
--- a/lib/docsplit/transparent_pdfs.rb
+++ b/lib/docsplit/transparent_pdfs.rb
@@ -8,17 +8,20 @@ module TransparentPDFs
# through further extraction.
def ensure_pdfs(docs)
[docs].flatten.map do |doc|
- ext = File.extname(doc)
- if ext.downcase == '.pdf' || File.open(doc, &:readline) =~ /\A\%PDF-\d+(\.\d+)?$/
+ if is_pdf?(doc)
doc
else
tempdir = File.join(Dir.tmpdir, 'docsplit')
extract_pdf([doc], {:output => tempdir})
- File.join(tempdir, File.basename(doc, ext) + '.pdf')
+ File.join(tempdir, File.basename(doc, File.extname(doc)) + '.pdf')
end
end
end
+ def is_pdf?(doc)
+ File.extname(doc).downcase == '.pdf' || File.open(doc, 'rb', &:readline) =~ /\A\%PDF-\d+(\.\d+)?/
+ end
+
end
extend TransparentPDFs
diff --git a/test/fixtures/with_pdf_extension/actually_a_doc.pdf b/test/fixtures/with_pdf_extension/actually_a_doc.pdf
new file mode 100755
index 0000000000000000000000000000000000000000..e7f7abd551084910fd96299d7b82124e100e2d2f
GIT binary patch
literal 52224
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
literal 0
HcmV?d00001
diff --git a/test/fixtures/with_pdf_extension/actually_an_image.pdf b/test/fixtures/with_pdf_extension/actually_an_image.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..3f0ce525f0d98579003f1ef5c19c19aa73a0f52f
GIT binary patch
literal 34964
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