arquivo 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -2
- data/README.md +4 -4
- data/arquivo.gemspec +13 -8
- data/lib/arquivo.rb +33 -19
- data/lib/arquivo/dir.rb +52 -58
- data/lib/arquivo/extrato.rb +69 -39
- data/lib/arquivo/jpg.rb +57 -38
- data/lib/arquivo/mp3.rb +64 -41
- data/lib/arquivo/noise.rb +83 -31
- data/lib/arquivo/pdf.rb +103 -86
- data/lib/arquivo/version.rb +1 -1
- metadata +20 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed65dd3f514e1950093d139e190ba9104037dc1637f4aa63a879aa5a26e7707f
|
4
|
+
data.tar.gz: 75603c49ac01b58575ad63bba4aee0cf5265594fa41a95407aaa10125a717cf4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2440e3fa68fbc48bc7e34432bcbb0d832d6c530c3dbbe872b649cb9f7c6d830b7e04034621b8dbbc8a2ad506abce5587394b6334c324f9c1932fd0f09cdca2d4
|
7
|
+
data.tar.gz: dc20b52f6c192f51922637b96ebc8cc063178ce5812a8947aa0cc1f13c395cbba8698a0810d4269e70af9693cbbaf72ce54905726a46c74c8c958e7615a9a536
|
data/Gemfile.lock
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
arquivo (0.2.
|
4
|
+
arquivo (0.2.2)
|
5
5
|
fastimage (~> 2.1)
|
6
6
|
google-api-client (~> 0.34)
|
7
7
|
pdf-reader (~> 2.3)
|
8
8
|
thor (~> 0.1)
|
9
|
+
yard (~> 0.9)
|
9
10
|
|
10
11
|
GEM
|
11
12
|
remote: https://rubygems.org/
|
@@ -19,7 +20,7 @@ GEM
|
|
19
20
|
faraday (0.17.1)
|
20
21
|
multipart-post (>= 1.2, < 3)
|
21
22
|
fastimage (2.1.7)
|
22
|
-
google-api-client (0.36.
|
23
|
+
google-api-client (0.36.2)
|
23
24
|
addressable (~> 2.5, >= 2.5.1)
|
24
25
|
googleauth (~> 0.9)
|
25
26
|
httpclient (>= 2.8.1, < 3.0)
|
@@ -64,6 +65,7 @@ GEM
|
|
64
65
|
thor (0.20.3)
|
65
66
|
ttfunk (1.5.1)
|
66
67
|
uber (0.1.0)
|
68
|
+
yard (0.9.20)
|
67
69
|
|
68
70
|
PLATFORMS
|
69
71
|
ruby
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Arquivo
|
2
2
|
|
3
|
-
Processa documentos do condominio ph1341c118 para arquivo.
|
3
|
+
Processa documentos do condominio ph1341c118 para arquivo. Pode tambem segmentar PDFs e MINUTAS. Tendo os documentos em pastas separadas, pode ainda criar arquivos apropriados.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -20,9 +20,9 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
$ arquivo
|
24
|
-
$ arquivo pdf
|
25
|
-
$ arquivo
|
23
|
+
$ arquivo mp3 MINUTA # processa MINUTA criando pasta com segmentos para arquivo
|
24
|
+
$ arquivo pdf EXTRATO # processa EXTRATO criando pasta com documentos para arquivo
|
25
|
+
$ arquivo dir PASTA # processa faturas/recibos/extratos/minutas e cria arquivos c118
|
26
26
|
|
27
27
|
## Development
|
28
28
|
|
data/arquivo.gemspec
CHANGED
@@ -5,15 +5,19 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
5
|
require 'arquivo/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
|
-
spec.name
|
9
|
-
spec.version
|
10
|
-
spec.authors
|
11
|
-
spec.email
|
12
|
-
spec.homepage
|
8
|
+
spec.name = 'arquivo'
|
9
|
+
spec.version = Arquivo::VERSION
|
10
|
+
spec.authors = ['Hernâni Rodrigues Vaz']
|
11
|
+
spec.email = ['hernanirvaz@gmail.com']
|
12
|
+
spec.homepage = 'https://github.com/ph1341c118/arquivo'
|
13
13
|
|
14
|
-
spec.summary
|
15
|
-
|
16
|
-
spec.
|
14
|
+
spec.summary = 'Processa documentos do condominio ph1341c118 ' \
|
15
|
+
'para arquivo.'
|
16
|
+
spec.description = spec.summary
|
17
|
+
spec.description += ' Pode tambem segmentar PDFs e MINUTAS. ' \
|
18
|
+
'Tendo os documentos em pastas separadas, pode ainda ' \
|
19
|
+
'criar arquivos apropriados.'
|
20
|
+
spec.license = 'MIT'
|
17
21
|
|
18
22
|
spec.metadata['homepage_uri'] = spec.homepage
|
19
23
|
spec.metadata['yard.run'] = 'yard'
|
@@ -36,4 +40,5 @@ Gem::Specification.new do |spec|
|
|
36
40
|
spec.add_dependency 'google-api-client', '~> 0.34'
|
37
41
|
spec.add_dependency 'pdf-reader', '~> 2.3'
|
38
42
|
spec.add_dependency 'thor', '~> 0.1'
|
43
|
+
spec.add_dependency 'yard', '~> 0.9'
|
39
44
|
end
|
data/lib/arquivo.rb
CHANGED
@@ -7,36 +7,47 @@ require 'arquivo/pdf'
|
|
7
7
|
require 'arquivo/jpg'
|
8
8
|
require 'arquivo/mp3'
|
9
9
|
|
10
|
+
# @author Hernani Rodrigues Vaz
|
11
|
+
# processa documentos do condominio ph1341c118 para arquivo;
|
12
|
+
# pode tambem segmentar PDFs e MINUTAS;
|
13
|
+
# tendo os documentos em pastas separadas,
|
14
|
+
# pode ainda criar arquivos apropriados.
|
10
15
|
module Arquivo
|
11
16
|
class Error < StandardError; end
|
12
17
|
|
13
|
-
# CLI
|
18
|
+
# @abstract CLI tarefas segmentar e arquivar
|
14
19
|
class CLI < Thor
|
15
20
|
desc 'mp3 MINUTA', 'processa MINUTA criando pasta ' \
|
16
21
|
'com segmentos para arquivo'
|
17
22
|
option :tempos, type: :array, default: [],
|
18
23
|
desc: 'lista tempos para segmentar MINUTA, ex: [[h:]m:]s'
|
19
|
-
|
20
|
-
|
24
|
+
# segmenta minuta segundo lista tempos
|
25
|
+
#
|
26
|
+
# @param [String] minuta ficheiro audio a segmentar
|
27
|
+
def mp3(minuta)
|
28
|
+
return unless File.exist?(minuta) && File.ftype(minuta) == 'file'
|
21
29
|
|
22
|
-
f = C118mp3.new(
|
23
|
-
return unless f.
|
30
|
+
f = C118mp3.new(minuta, options)
|
31
|
+
return unless f.segmenta_minuta?
|
24
32
|
|
25
33
|
system "mkdir -p #{f.base}"
|
26
|
-
f.
|
34
|
+
f.segmenta_minuta(options[:tempos])
|
27
35
|
end
|
28
36
|
|
29
37
|
desc 'pdf EXTRATO', 'processa EXTRATO criando pasta ' \
|
30
38
|
'com documentos para arquivo'
|
31
|
-
|
32
|
-
|
39
|
+
# segmenta extrato limpando publicidade
|
40
|
+
#
|
41
|
+
# @param [String] extrato pdf a segmentar
|
42
|
+
def pdf(extrato)
|
43
|
+
return unless File.exist?(extrato) && File.ftype(extrato) == 'file'
|
33
44
|
|
34
|
-
f = C118pdf.new(
|
45
|
+
f = C118pdf.new(extrato, options)
|
35
46
|
return unless f.processa_extrato?
|
36
47
|
|
37
48
|
system "mkdir -p #{f.base}"
|
38
49
|
# extrato contem conta c118
|
39
|
-
if f.
|
50
|
+
if f.pagina_extrato?
|
40
51
|
f.processa_extrato(0)
|
41
52
|
else
|
42
53
|
f.split
|
@@ -44,25 +55,28 @@ module Arquivo
|
|
44
55
|
end
|
45
56
|
|
46
57
|
desc 'dir PASTA', 'processa faturas/recibos/extratos/minutas ' \
|
47
|
-
'
|
58
|
+
'e cria arquivos c118'
|
48
59
|
option :fuzz, type: :numeric, default: 29,
|
49
60
|
desc: 'fuzz trim jpg N-1, escolhe menor -> scanned pdf'
|
50
61
|
option :quality, type: :numeric, default: 15,
|
51
62
|
desc: 'compress jpg N% -> scanned pdf (less=low quality)'
|
52
63
|
|
53
|
-
option :
|
54
|
-
|
55
|
-
option :sound, type: :numeric, default: 1
|
56
|
-
desc: '
|
64
|
+
option :threshold, type: :numeric, default: 9,
|
65
|
+
desc: 'limiar maximo para silencio, 0% = silencio puro'
|
66
|
+
option :sound, type: :numeric, default: 1,
|
67
|
+
desc: 'segundos de som para terminar silencio'
|
68
|
+
|
57
69
|
option :amount, type: :numeric, default: 0.00001,
|
58
70
|
desc: 'qtd ruido a ser removido'
|
59
71
|
option :rate, type: :numeric, default: 16,
|
60
72
|
desc: 'sample rate - radio-16k, CD-44.1k, PC-48k, pro-96k'
|
73
|
+
# arquiva pasta de documentos c118
|
74
|
+
#
|
75
|
+
# @param [String] pasta contem os documentos para arquivar
|
76
|
+
def dir(pasta)
|
77
|
+
return unless File.ftype(pasta) == 'directory'
|
61
78
|
|
62
|
-
|
63
|
-
return unless File.ftype(fdir) == 'directory'
|
64
|
-
|
65
|
-
C118dir.new(fdir).processa_pasta(fdir, options)
|
79
|
+
C118dir.new(pasta, options).processa_pasta(pasta)
|
66
80
|
end
|
67
81
|
end
|
68
82
|
end
|
data/lib/arquivo/dir.rb
CHANGED
@@ -7,47 +7,32 @@ require 'googleauth/stores/file_token_store'
|
|
7
7
|
require 'arquivo/noise'
|
8
8
|
|
9
9
|
module Arquivo
|
10
|
-
O1 = '2>/dev/null'
|
11
|
-
O2 = '1>/dev/null 2>&1'
|
12
|
-
|
10
|
+
# O1 = '2>/dev/null'
|
11
|
+
# O2 = '1>/dev/null 2>&1'
|
12
|
+
O1 = ''
|
13
|
+
O2 = ''
|
14
|
+
AT = ['.mp3', '.m4a', '.wav', '.sox'].freeze
|
13
15
|
|
14
|
-
#
|
16
|
+
# permite processar e arquivar pasta com documentos c118
|
15
17
|
class C118dir < Enumerator
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
attr_reader :item
|
20
|
-
|
21
|
-
# @return [Hash] dados (faturas/recibos) de c118-contas
|
22
|
-
attr_reader :dados
|
23
|
-
# @return [Float] maximo segundos de silencio encontrados
|
24
|
-
attr_reader :silence
|
25
|
-
# @return [String] noiseprof do silencio encontrado
|
26
|
-
attr_reader :noiseprof
|
27
|
-
|
28
|
-
# @return [String] base nome ficheiros finais (pdf, tar.gz)
|
29
|
-
attr_reader :base
|
30
|
-
|
31
|
-
# @return [C118dir] documentos c118
|
32
|
-
def initialize(pasta)
|
33
|
-
@items = Dir.glob(File.join(pasta, '*')).each
|
34
|
-
@base = File.basename(pasta, File.extname(pasta)) + '-' +
|
35
|
-
Date.today.strftime('%Y%m%d')
|
36
|
-
end
|
37
|
-
|
38
|
-
def processa_items(options)
|
18
|
+
# @!group processamento
|
19
|
+
# processa items duma pasta - sub-pastas recursivamente
|
20
|
+
def processa_items
|
39
21
|
n = 0
|
40
22
|
while next_item
|
41
23
|
if File.ftype(item) == 'directory'
|
42
|
-
C118dir.new(item).processa_pasta(item
|
24
|
+
C118dir.new(item, opcoes).processa_pasta(item)
|
43
25
|
else
|
44
|
-
processa_file(
|
26
|
+
processa_file(File.extname(item).downcase)
|
45
27
|
n += 1
|
46
28
|
end
|
47
29
|
end
|
48
30
|
processa_fim(n)
|
49
31
|
end
|
50
32
|
|
33
|
+
# cria ficheiros finais para arquivo
|
34
|
+
#
|
35
|
+
# @param [Numeric] num numero de documentos dentro do arquivo
|
51
36
|
def processa_fim(num)
|
52
37
|
return unless num.positive?
|
53
38
|
|
@@ -56,56 +41,65 @@ module Arquivo
|
|
56
41
|
else
|
57
42
|
"rm -f #{base}.*;pdftk tmp/stamped*.pdf cat output #{base}.pdf"
|
58
43
|
end
|
59
|
-
# ;rm -rf tmp
|
60
44
|
system cmd + ";cd tmp/zip;tar cf ../../#{base}.tar *" \
|
61
|
-
";cd ../..;gzip --best #{base}.tar
|
45
|
+
";cd ../..;gzip --best #{base}.tar" \
|
46
|
+
'' # ';rm -rf tmp'
|
62
47
|
|
63
48
|
puts "#{base} (#{num})"
|
64
49
|
end
|
65
50
|
|
66
|
-
|
51
|
+
# processa ficheiro JPG, PDF ou AUDIO
|
52
|
+
#
|
53
|
+
# @param [String] ext tipo ficheiro
|
54
|
+
def processa_file(ext)
|
55
|
+
opt = opcoes
|
67
56
|
case ext
|
68
|
-
when '.jpg' then C118jpg.new(item).processa_jpg(
|
69
|
-
when '.pdf' then C118pdf.new(item).processa_pdf(
|
70
|
-
when *
|
71
|
-
C118mp3.new(item).processa_mp3(options, noiseprof)
|
57
|
+
when '.jpg' then C118jpg.new(item, opt).processa_jpg(dados)
|
58
|
+
when '.pdf' then C118pdf.new(item, opt).processa_pdf(dados)
|
59
|
+
when *AT then C118mp3.new(item, opt).processa_mp3(noiseprof)
|
72
60
|
else
|
73
|
-
puts "erro: #{
|
61
|
+
puts "erro: #{ext} nao posso processar este tipo de dicheiro"
|
74
62
|
end
|
75
63
|
end
|
76
64
|
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
@item = nil
|
82
|
-
end
|
83
|
-
|
84
|
-
def processa_pasta(pasta, options)
|
65
|
+
# processa conteudo duma pasta
|
66
|
+
#
|
67
|
+
# @param pasta (see CLI#dir)
|
68
|
+
def processa_pasta(pasta)
|
85
69
|
unless File.ftype(items.peek) == 'directory'
|
86
|
-
@dados = {}
|
87
|
-
obtem_dados(pasta)
|
88
|
-
|
89
|
-
@silence = 0.0
|
90
70
|
system 'mkdir -p tmp/zip'
|
91
|
-
|
71
|
+
obtem_dados(pasta)
|
72
|
+
obtem_noiseprof(pasta)
|
92
73
|
end
|
93
|
-
processa_items
|
74
|
+
processa_items
|
94
75
|
end
|
95
76
|
|
96
|
-
|
97
|
-
|
77
|
+
# @return [String] proximo item dentro da pasta
|
78
|
+
def next_item
|
79
|
+
@item = items.next
|
80
|
+
rescue StopIteration
|
81
|
+
@item = nil
|
82
|
+
end
|
98
83
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
84
|
+
# @!group dados online
|
85
|
+
# @param pasta (see CLI#dir)
|
86
|
+
# @return [Hash] dados oficiais para reclassificacao de faturas e recibos
|
87
|
+
def obtem_dados(pasta)
|
88
|
+
@dados = {}
|
89
|
+
# somente faturas e recibos necessitam reclassificacao
|
90
|
+
return unless /fac?tura/i.match?(pasta) || /recibo/i.match?(pasta)
|
91
|
+
|
92
|
+
# sheet c118-contas
|
93
|
+
dg = '1PbiMrtTtqGztZMhe3AiJbDS6NQE9o3hXebnQEFdt954'
|
94
|
+
# range dos dados (faturas/recibos)
|
95
|
+
sh = (/fac?tura/i.match?(pasta) ? 'rft' : 'rrc') + '!A2:E'
|
96
|
+
@dados = c118_sheets.get_spreadsheet_values(dg, sh).values
|
103
97
|
.group_by { |k| k[0][/\w+/] }
|
104
98
|
rescue StandardError
|
105
99
|
@dados = {}
|
106
100
|
end
|
107
101
|
|
108
|
-
# assegura credenciais validas, obtidas dum
|
102
|
+
# assegura credenciais validas, obtidas dum ficheiro de credencias
|
109
103
|
#
|
110
104
|
# @return [Google::Apis::SheetsV4::SheetsService] c118 sheets_v4
|
111
105
|
def c118_sheets
|
data/lib/arquivo/extrato.rb
CHANGED
@@ -3,64 +3,82 @@
|
|
3
3
|
require 'pdf-reader'
|
4
4
|
|
5
5
|
module Arquivo
|
6
|
-
#
|
6
|
+
# permite processar documentos PDF
|
7
7
|
class C118pdf < String
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
8
|
+
# @return [String] nome do documento
|
9
|
+
attr_reader :file
|
10
|
+
# @return [String] extensao do documento
|
11
|
+
attr_reader :ext
|
12
|
+
# @return [String] base do documento
|
13
|
+
attr_reader :base
|
14
|
+
# @return [Integer] tamanho do documento
|
15
|
+
attr_reader :size
|
16
|
+
# @return [Hash] opcoes parametrizar JPG
|
17
|
+
attr_reader :opcoes
|
18
|
+
# @return [String] id do documento ft/rc/ex/sc <numero>
|
19
|
+
attr_reader :id
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
# @return [Array<Integer>] lista paginas do extrato
|
22
|
+
attr_reader :paginas
|
23
|
+
# @return [String] texto pagina
|
24
|
+
attr_reader :pagina
|
25
|
+
# @return [String] nome extrato
|
26
|
+
attr_reader :nome
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
28
|
+
# @param [String] pdf PDF c118
|
29
|
+
# @param opt (see C118jpg#initialize)
|
30
|
+
# @option opt (see C118jpg#initialize)
|
31
|
+
# @return [C118pdf] PDF c118
|
32
|
+
def initialize(pdf, opt)
|
33
|
+
@file = pdf
|
34
|
+
@ext = File.extname(pdf).downcase
|
35
|
+
@base = File.basename(pdf, File.extname(pdf))
|
36
|
+
@id = @base[/\w+/]
|
37
|
+
@size = File.size(pdf)
|
38
|
+
@opcoes = opt
|
33
39
|
end
|
34
40
|
|
41
|
+
# @!group segmentacao
|
42
|
+
# segmenta extrato limpando publicidade
|
43
|
+
#
|
44
|
+
# @param [Integer] cnt contador pagina em processamento
|
35
45
|
def processa_extrato(cnt)
|
36
46
|
cnt += 1
|
37
|
-
@paginas << cnt if
|
47
|
+
@paginas << cnt if pagina_extrato?
|
38
48
|
if proxima_pagina
|
39
|
-
faz_extrato if
|
49
|
+
faz_extrato if novo_extrato?
|
40
50
|
processa_extrato(cnt)
|
41
51
|
else
|
42
52
|
faz_extrato
|
43
53
|
end
|
44
54
|
end
|
45
55
|
|
46
|
-
|
47
|
-
|
56
|
+
# @return [Boolean] posso segmentar extrato?
|
57
|
+
def processa_extrato?
|
58
|
+
return true if ext == '.pdf' && size.positive? && !File.exist?(base) &&
|
59
|
+
first_pagina?
|
60
|
+
|
61
|
+
if File.exist?(base)
|
62
|
+
puts "erro: #{base} pasta ja existe"
|
63
|
+
else
|
64
|
+
puts "erro: #{file} nao consigo obter primeira pagina do EXTRATO"
|
65
|
+
end
|
66
|
+
false
|
48
67
|
end
|
49
68
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
"-sPageList=#{paginas.join(',')} \"#{file}\" #{O2}"
|
54
|
-
puts "#{nome}-extrato"
|
55
|
-
proximo_extrato
|
69
|
+
# @return [Boolean] primeira pagina de extrato?
|
70
|
+
def novo_extrato?
|
71
|
+
pagina_extrato? && pagina.match?(/extrato +combinado/i)
|
56
72
|
end
|
57
73
|
|
58
|
-
|
74
|
+
# @return [Boolean] pagina de extrato?
|
75
|
+
def pagina_extrato?
|
59
76
|
pagina.include?('45463760224')
|
60
77
|
end
|
61
78
|
|
62
|
-
|
63
|
-
|
79
|
+
# @return [Boolean] primeira pagina?
|
80
|
+
def first_pagina?
|
81
|
+
leitor && proxima_pagina && nome_extrato
|
64
82
|
end
|
65
83
|
|
66
84
|
# @return [Enumerator::Lazy] leitor pdf
|
@@ -77,19 +95,31 @@ module Arquivo
|
|
77
95
|
@pagina = nil
|
78
96
|
end
|
79
97
|
|
80
|
-
|
98
|
+
# @return [String] nome proximo extrato
|
99
|
+
def nome_extrato
|
81
100
|
return false unless pagina
|
82
101
|
|
83
102
|
@paginas = []
|
84
103
|
n = pagina.scan(%r{N\. *(\d+)/(\d+)}).flatten
|
85
|
-
@nome = "ex#{n[0]
|
104
|
+
@nome = n.empty? ? nil : "ex#{n[0][/\d{2}$/]}#{n[1]}"
|
86
105
|
rescue StandardError
|
87
106
|
@nome = nil
|
88
107
|
end
|
89
108
|
|
109
|
+
# cria PDF do extrato
|
110
|
+
def faz_extrato
|
111
|
+
system "#{ghostscript} " \
|
112
|
+
"-sOutputFile=#{base}/#{nome}-extrato.pdf " \
|
113
|
+
"-sPageList=#{paginas.join(',')} \"#{file}\" #{O2}"
|
114
|
+
puts "#{nome}-extrato"
|
115
|
+
nome_extrato
|
116
|
+
end
|
117
|
+
|
118
|
+
# segmenta PDF pelas suas paginas
|
90
119
|
def split
|
91
120
|
system "pdftk #{file} burst output #{base}/pg%04d-#{base}.pdf;" \
|
92
121
|
"rm -f #{base}/*.txt"
|
122
|
+
puts "#{base}-split"
|
93
123
|
end
|
94
124
|
end
|
95
125
|
end
|
data/lib/arquivo/jpg.rb
CHANGED
@@ -15,64 +15,83 @@ module Arquivo
|
|
15
15
|
# Factor 1.04 creates 2*2% borders,
|
16
16
|
FB = 1.04
|
17
17
|
|
18
|
-
#
|
18
|
+
# permite processar documentos em imagens JPG
|
19
19
|
class C118jpg < String
|
20
|
-
# @return [String] nome do
|
20
|
+
# @return [String] nome do documento
|
21
21
|
attr_reader :file
|
22
|
-
# @return [String] extensao do
|
22
|
+
# @return [String] extensao do documento
|
23
23
|
attr_reader :ext
|
24
|
-
# @return [String] base do
|
24
|
+
# @return [String] base do documento
|
25
25
|
attr_reader :base
|
26
|
-
# @return [
|
27
|
-
attr_reader :key
|
28
|
-
# @return [Integer] tamanho do jpg
|
26
|
+
# @return [Integer] tamanho do documento
|
29
27
|
attr_reader :size
|
28
|
+
# @return [Hash] opcoes parametrizar JPG
|
29
|
+
attr_reader :opcoes
|
30
|
+
# @return [String] id do documento ft/rc/ex/sc <numero>
|
31
|
+
attr_reader :id
|
30
32
|
|
31
|
-
# @
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
# @param [String] jpg JPG c118
|
34
|
+
# @param [Hash] opt parametrizar JPG
|
35
|
+
# @option opt [Numeric] :fuzz (29) trim jpg N-1, escolhe menor ->
|
36
|
+
# scanned pdf
|
37
|
+
# @option opt [Numeric] :quality (15) compress jpg N% -> scanned pdf
|
38
|
+
# (less=low quality)
|
39
|
+
# @return [C118jpg] JPG c118
|
40
|
+
def initialize(jpg, opt)
|
41
|
+
@file = jpg
|
42
|
+
@ext = File.extname(jpg).downcase
|
43
|
+
@base = File.basename(jpg, File.extname(jpg))
|
44
|
+
@id = @base[/\w+/]
|
45
|
+
@size = File.size(jpg)
|
46
|
+
@opcoes = opt
|
38
47
|
end
|
39
48
|
|
40
|
-
|
41
|
-
|
49
|
+
# @!group processamento
|
50
|
+
# apara jpg e converte em pdf para arquivo
|
51
|
+
#
|
52
|
+
# @param dad (see C118pdf#processa_pdf)
|
53
|
+
def processa_jpg(dad)
|
54
|
+
apara.pdf.final(dad[id]).marca
|
42
55
|
end
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
def parm_qualidade(options)
|
50
|
-
"-quality #{options[:quality]}% -compress jpeg"
|
51
|
-
end
|
52
|
-
|
53
|
-
def trim(options)
|
54
|
-
f = options[:fuzz]
|
57
|
+
# @return [C118jpg] jpg com melhor aparado
|
58
|
+
def apara
|
59
|
+
f = opcoes[:fuzz]
|
55
60
|
h = {}
|
56
|
-
#
|
61
|
+
# aparar borders ao maximo
|
57
62
|
while f >= 1
|
58
|
-
|
59
|
-
h[
|
63
|
+
o = "tmp/#{id}-#{f}.jpg"
|
64
|
+
h[o] = size_aparado(f, o)
|
60
65
|
f -= 4
|
61
66
|
end
|
62
67
|
m = h.min_by { |_, v| v }
|
63
|
-
m[1].between?(LT, size) ? C118jpg.new(
|
68
|
+
m[1].between?(LT, size) ? C118jpg.new(m[0], opcoes) : self
|
64
69
|
end
|
65
70
|
|
66
|
-
|
67
|
-
|
68
|
-
system "convert \"#{file}\" #{
|
69
|
-
"-format pdf tmp/#{
|
71
|
+
# @return [C118pdf] pdf com jpg processada dentro
|
72
|
+
def pdf
|
73
|
+
system "convert \"#{file}\" #{oa4} #{oqualidade} " \
|
74
|
+
"-format pdf tmp/#{id}-trimed.pdf #{O2}"
|
75
|
+
|
76
|
+
C118pdf.new("tmp/#{id}-trimed.pdf", opcoes)
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param [Numeric] fuzz fuzziness actual em processamento
|
80
|
+
# @param [String] out jpg aparada
|
81
|
+
# @return [Numeric] tamanho da jpg aparada
|
82
|
+
def size_aparado(fuzz, out)
|
83
|
+
system "convert \"#{file}\" -fuzz #{fuzz}% -trim +repage " \
|
84
|
+
"#{oqualidade} #{out} #{O2}"
|
85
|
+
File.size(out)
|
86
|
+
end
|
70
87
|
|
71
|
-
|
72
|
-
|
88
|
+
# @return [String] opcoes comprimir jpg
|
89
|
+
def oqualidade
|
90
|
+
"-quality #{opcoes[:quality]}% -compress jpeg"
|
73
91
|
end
|
74
92
|
|
75
|
-
|
93
|
+
# @return [String] opcoes centrar jpg em canvas A4
|
94
|
+
def oa4
|
76
95
|
# image dimensions in pixels.
|
77
96
|
x, y = FastImage.size(file)
|
78
97
|
|
data/lib/arquivo/mp3.rb
CHANGED
@@ -1,71 +1,94 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Arquivo
|
4
|
-
#
|
4
|
+
# permite processar documentos em audio
|
5
5
|
class C118mp3 < String
|
6
|
-
# @return [String] nome do
|
6
|
+
# @return [String] nome do documento
|
7
7
|
attr_reader :file
|
8
|
-
# @return [String] extensao do
|
8
|
+
# @return [String] extensao do documento
|
9
9
|
attr_reader :ext
|
10
|
-
# @return [String] base do
|
10
|
+
# @return [String] base do documento
|
11
11
|
attr_reader :base
|
12
|
-
# @return [
|
12
|
+
# @return [Integer] tamanho do documento
|
13
13
|
attr_reader :size
|
14
|
+
# @return [Hash] opcoes parametrizar MINUTA
|
15
|
+
attr_reader :opcoes
|
14
16
|
|
15
|
-
# @
|
16
|
-
|
17
|
-
|
18
|
-
# @
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@
|
23
|
-
@
|
17
|
+
# @param [String] mp3 MP3 c118
|
18
|
+
# @param [Hash] opt parametrizar MINUTA
|
19
|
+
# @option opt [Numeric] :amount (0.00001) qtd ruido a ser removido,
|
20
|
+
# @option opt [Numeric] :rate (16) sample rate - radio-16k, CD-44.1k,
|
21
|
+
# PC-48k, pro-96k
|
22
|
+
# @return [C118mp3] MP3 c118
|
23
|
+
def initialize(mp3, opt)
|
24
|
+
@file = mp3
|
25
|
+
@ext = File.extname(mp3).downcase
|
26
|
+
@base = File.basename(mp3, File.extname(mp3))
|
27
|
+
@size = `soxi -V0 -D #{mp3} #{O1}`.to_f
|
28
|
+
@opcoes = opt
|
24
29
|
end
|
25
30
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
cmd += "rate -v #{options[:rate]}k channels 1"
|
33
|
-
system "sox -G #{file} tmp/zip/#{base}.mp3 #{cmd} #{O2}"
|
31
|
+
# @!group processamento
|
32
|
+
# Processa mp3 para arquivo
|
33
|
+
#
|
34
|
+
# @param [String] npr perfil do silencio
|
35
|
+
def processa_mp3(npr)
|
36
|
+
system "sox -G #{file} tmp/zip/#{base}.mp3 #{onoise(npr)}#{orate} #{O2}"
|
34
37
|
end
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
# @param npr (see #processa_mp3)
|
40
|
+
# @return [String] opcoes reducao ruido de fundo
|
41
|
+
def onoise(npr)
|
42
|
+
npr ? "noisered #{npr} #{format('%<v>.9f', v: opcoes[:amount])} " : ''
|
43
|
+
end
|
40
44
|
|
41
|
-
|
42
|
-
|
43
|
-
|
45
|
+
# @return [String] opcoes sample rate & channels
|
46
|
+
def orate
|
47
|
+
"rate -v #{opcoes[:rate]}k channels 1"
|
48
|
+
end
|
44
49
|
|
45
|
-
|
50
|
+
# @!group segmentacao
|
51
|
+
# Segmenta minuta segundo lista tempos
|
52
|
+
#
|
53
|
+
# @param [Array] tempos lista tempos para segmentar minuta
|
54
|
+
# @example tempos
|
55
|
+
# ["120", "10:11", "[[h:]m:]s", ...]
|
56
|
+
def segmenta_minuta(tempos)
|
57
|
+
system cmd_segmenta(['0'] + tempos, 0, '')
|
46
58
|
end
|
47
59
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
60
|
+
# @param [Integer] pse numero do segmento em processamento
|
61
|
+
# @return [String] nome do segmento
|
62
|
+
def nome_segmento(pse)
|
63
|
+
"s#{format('%<v>02d', v: pse)}-#{base[/\d{8}/]}#{base[/-\w+/]}"
|
52
64
|
end
|
53
65
|
|
54
|
-
|
55
|
-
|
66
|
+
# @param tempos (see #segmenta_minuta)
|
67
|
+
# @param pse (see #nome_segmento)
|
68
|
+
# @param [String] cmd comando para segmentar minuta
|
69
|
+
# @return [String] comando para segmentar minuta
|
70
|
+
def cmd_segmenta(tempos, pse, cmd)
|
71
|
+
return cmd[1..-1] unless pse < tempos.size
|
72
|
+
|
73
|
+
o = nome_segmento(pse)
|
74
|
+
cmd += ";sox #{file} #{base}/#{o}#{ext} trim #{tempos[pse]}"
|
75
|
+
pse += 1
|
76
|
+
cmd += " =#{tempos[pse]}" if pse < tempos.size
|
77
|
+
puts o
|
78
|
+
|
79
|
+
cmd_segmenta(tempos, pse, cmd + " #{O2}")
|
56
80
|
end
|
57
81
|
|
58
|
-
|
59
|
-
|
60
|
-
|
82
|
+
# @return [Boolean] posso segmentar minuta?
|
83
|
+
def segmenta_minuta?
|
84
|
+
return true if AT.include?(ext) && size.positive? && !File.exist?(base)
|
61
85
|
|
62
86
|
if File.exist?(base)
|
63
87
|
puts "erro: #{base} pasta ja existe"
|
64
88
|
else
|
65
89
|
puts 'erro: so consigo processar minutas com som ' \
|
66
|
-
"e tipo #{
|
90
|
+
"e do tipo #{AT}"
|
67
91
|
end
|
68
|
-
|
69
92
|
false
|
70
93
|
end
|
71
94
|
end
|
data/lib/arquivo/noise.rb
CHANGED
@@ -1,54 +1,106 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Arquivo
|
4
|
-
#
|
4
|
+
# permite processar e arquivar pasta com documentos c118
|
5
5
|
class C118dir < Enumerator
|
6
|
-
|
7
|
-
|
6
|
+
# @return [Enumerator] items dentro duma pasta
|
7
|
+
attr_reader :items
|
8
|
+
# @return [String] base nome ficheiros para arquivo (pdf, tar.gz)
|
9
|
+
attr_reader :base
|
10
|
+
# @return [Hash] parametrizar JPG, MINUTA
|
11
|
+
attr_reader :opcoes
|
12
|
+
# @return [String] documento c118
|
13
|
+
attr_reader :item
|
8
14
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
15
|
+
# @return (see #obtem_dados)
|
16
|
+
attr_reader :dados
|
17
|
+
# @return (see #obtem_noiseprof)
|
18
|
+
attr_reader :noiseprof
|
13
19
|
|
14
|
-
|
20
|
+
# @param pasta (see CLI#dir)
|
21
|
+
# @param [Hash] opt parametrizar JPG, MINUTA
|
22
|
+
# @option opt [Numeric] :fuzz (29) trim jpg N-1, escolhe menor ->
|
23
|
+
# scanned pdf
|
24
|
+
# @option opt [Numeric] :quality (15) compress jpg N% -> scanned pdf
|
25
|
+
# (less=low quality)
|
26
|
+
# @option opt [Numeric] :threshold (9) limiar maximo para silencio,
|
27
|
+
# 0% = silencio puro
|
28
|
+
# @option opt [Numeric] :sound (1) segundos de som para terminar silencio
|
29
|
+
# @option opt [Numeric] :amount (0.00001) qtd ruido a ser removido,
|
30
|
+
# @option opt [Numeric] :rate (16) sample rate - radio-16k, CD-44.1k,
|
31
|
+
# PC-48k, pro-96k
|
32
|
+
# @return [C118dir] pasta de documentos c118
|
33
|
+
def initialize(pasta, opt)
|
34
|
+
@items = Dir.glob(File.join(pasta, '*')).each
|
35
|
+
@base = File.basename(pasta, File.extname(pasta)) + '-' +
|
36
|
+
Date.today.strftime('%Y%m%d')
|
37
|
+
@opcoes = opt
|
15
38
|
end
|
16
39
|
|
17
|
-
|
18
|
-
|
40
|
+
# @!group perfil silencio
|
41
|
+
# @param pasta (see CLI#dir)
|
42
|
+
# @return [String] perfil do maior silencio inicial de todos segmentos audio
|
43
|
+
def obtem_noiseprof(pasta)
|
44
|
+
return unless /minuta/i.match?(pasta)
|
19
45
|
|
20
|
-
|
21
|
-
|
22
|
-
seg[2] = duracao(o)
|
23
|
-
return unless seg[2].positive?
|
46
|
+
l = obtem_segmentos(pasta)
|
47
|
+
return unless l.size.positive?
|
24
48
|
|
25
|
-
|
26
|
-
|
49
|
+
t = -1
|
50
|
+
m = ['', 0]
|
51
|
+
m = obtem_maximo_silencio(l, t += 1) while noisy?(m, t)
|
27
52
|
|
28
|
-
|
29
|
-
|
30
|
-
# obter noiseprof do silencio encontrado
|
31
|
-
system "sox #{trm} -n noiseprof #{o} #{O2}"
|
53
|
+
cria_noiseprof(m)
|
54
|
+
end
|
32
55
|
|
33
|
-
|
34
|
-
|
35
|
-
|
56
|
+
# @param [Array<String, Float>] seg segmento, duracao silencio inicial
|
57
|
+
# @param thr (see #obtem_maximo_silencio)
|
58
|
+
# @return [Boolean] segmento audio tem som ou silencio no inicio
|
59
|
+
def noisy?(seg, thr)
|
60
|
+
thr < opcoes[:threshold] && seg[1] <= opcoes[:sound]
|
36
61
|
end
|
37
62
|
|
38
|
-
|
39
|
-
|
63
|
+
# @param [Array] lsg lista segmentos audio com duracoes
|
64
|
+
# @param [Numeric] thr limiar para silencio em processamento
|
65
|
+
# @return [Array<String, Float>] segmento com maior duracao silencio inicial
|
66
|
+
def obtem_maximo_silencio(lsg, thr)
|
67
|
+
lsg.sort.map { |e| obtem_silencio(e, thr) }.max_by { |_, s| s }
|
40
68
|
end
|
41
69
|
|
42
|
-
|
70
|
+
# @param [Array<String, Float>] seg segmento audio, duracao
|
71
|
+
# @param thr (see #obtem_maximo_silencio)
|
72
|
+
# @return [Array<String, Float>] segmento audio, duracao silencio inicial
|
73
|
+
def obtem_silencio(seg, thr)
|
43
74
|
o = "tmp/thr-#{File.basename(seg[0])}"
|
44
|
-
system "sox #{seg[0]} #{o} silence 1 #{
|
45
|
-
|
75
|
+
system "sox #{seg[0]} #{o} silence 1 #{opcoes[:sound]}t #{thr}% #{O2}"
|
76
|
+
|
77
|
+
[seg[0], (seg[1] - duracao(o)).round(2, half: :down)]
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param seg (see #noisy?)
|
81
|
+
# @return [String] perfil sonoro do silencio inicial dum segmento
|
82
|
+
def cria_noiseprof(seg)
|
83
|
+
return unless seg[1] > opcoes[:sound]
|
84
|
+
|
85
|
+
o = "tmp/noiseprof-#{File.basename(seg[0], File.extname(seg[0]))}"
|
86
|
+
# obter noiseprof do silencio no inicio
|
87
|
+
system "sox #{seg[0]} -n trim 0 #{seg[1]} noiseprof #{o} #{O2}"
|
88
|
+
|
89
|
+
# so noiseprof validos sao devolvidos
|
90
|
+
@noiseprof = File.size?(o).positive? ? o : nil
|
91
|
+
end
|
46
92
|
|
47
|
-
|
93
|
+
# @param pasta (see CLI#dir)
|
94
|
+
# @return [Array] lista segmentos audio com duracoes
|
95
|
+
def obtem_segmentos(pasta)
|
96
|
+
AT.map { |e| Dir.glob(File.join(pasta, 's[0-9][0-9]-*' + e)) }.flatten
|
97
|
+
.map { |s| [s, duracao(s)] }
|
48
98
|
end
|
49
99
|
|
50
|
-
|
51
|
-
|
100
|
+
# @param [String] audio ficheiro de audio
|
101
|
+
# @return [Float] duracao ficheiro audio em segundos
|
102
|
+
def duracao(audio)
|
103
|
+
`soxi -V0 -D #{audio} #{O1}`.to_f
|
52
104
|
end
|
53
105
|
end
|
54
106
|
end
|
data/lib/arquivo/pdf.rb
CHANGED
@@ -6,107 +6,103 @@ require 'i18n'
|
|
6
6
|
I18n.config.available_locales = :pt
|
7
7
|
|
8
8
|
module Arquivo
|
9
|
-
#
|
9
|
+
# permite processar documentos PDF
|
10
10
|
class C118pdf < String
|
11
|
-
#
|
12
|
-
|
13
|
-
#
|
14
|
-
|
15
|
-
# @
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
# @return [Array<Integer>] numeros pagina do extrato final
|
23
|
-
attr_reader :paginas
|
24
|
-
# @return [String] texto pagina pdf
|
25
|
-
attr_reader :pagina
|
26
|
-
# @return [String] nome extrato
|
27
|
-
attr_reader :nome
|
28
|
-
|
29
|
-
# @return [C118pdf] pdf c118
|
30
|
-
def initialize(fpdf)
|
31
|
-
@file = fpdf
|
32
|
-
@ext = File.extname(fpdf).downcase
|
33
|
-
@base = File.basename(fpdf, File.extname(fpdf))
|
34
|
-
@key = @base[/\w+/]
|
35
|
-
@size = File.size(fpdf)
|
36
|
-
end
|
37
|
-
|
38
|
-
def processa_pdf(options, dados)
|
39
|
-
# em caso de scanned pdf extract.trim.jpg -> trimed pdf
|
40
|
-
tpdf = jpg? ? extract.trim(options).converte(options) : self
|
11
|
+
# @!group processamento
|
12
|
+
# processa pdf para arquivo
|
13
|
+
#
|
14
|
+
# @param [Hash] dad dados oficiais para reclassificacao de faturas e recibos
|
15
|
+
# @example dad
|
16
|
+
# {"ft1901"=>[["ft1901","legal","assembleia","expediente","-1395"]],
|
17
|
+
# "ft1944"=>[["ft1944","banco","juro","dc3029998410","100"],
|
18
|
+
# ["ft1944","banco","irc","dc3029998410","-28"]]}
|
19
|
+
def processa_pdf(dad)
|
20
|
+
o = "tmp/#{id}-extract.jpg"
|
21
|
+
pdf = jpg?(o) ? C118jpg.new(o, opcoes).apara.pdf : self
|
41
22
|
|
42
23
|
# usar trimed pdf somente se for menor que original
|
43
|
-
(
|
44
|
-
end
|
45
|
-
|
46
|
-
def marca
|
47
|
-
o = "tmp/stamped-#{base[/-(\w+)/, 1]}-#{key}.pdf"
|
48
|
-
s = '2 2 moveto /Ubuntu findfont 7 scalefont ' \
|
49
|
-
"setfont (#{base}) show"
|
50
|
-
system "#{c118_gs} -sOutputFile=tmp/stamp-#{key}.pdf -c \"#{s}\";" \
|
51
|
-
"pdftk tmp/zip/#{base}.pdf " \
|
52
|
-
"stamp tmp/stamp-#{key}.pdf output #{o} #{O2}"
|
53
|
-
# puts key
|
24
|
+
(pdf.size < size ? pdf : self).final(dad[id]).marca
|
54
25
|
end
|
55
26
|
|
27
|
+
# @param [Array] kda lista dados para reclassificacao do documento
|
28
|
+
# @return [C118pdf] pdf totalmente processado
|
29
|
+
# @example kda-ft1901
|
30
|
+
# [["ft1901","legal","assembleia","expediente","-1395"]]
|
56
31
|
def final(kda)
|
57
|
-
|
32
|
+
stamp(kda)
|
58
33
|
o = "tmp/zip/#{base}.pdf"
|
59
34
|
|
60
|
-
system "#{
|
61
|
-
#
|
35
|
+
system "#{ghostscript} -sOutputFile=#{o} \"#{file}\" #{O2}"
|
36
|
+
# copia original se processado for maior
|
62
37
|
system "cp \"#{file}\" #{o}" if File.size(o) > size
|
63
38
|
|
64
|
-
C118pdf.new(o)
|
39
|
+
C118pdf.new(o, opcoes)
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param kda (see #final)
|
43
|
+
# @return [String] texto completo do selo
|
44
|
+
def stamp(kda)
|
45
|
+
stamp_base(kda)
|
46
|
+
return unless kda
|
47
|
+
|
48
|
+
stamp_digitos(kda)
|
49
|
+
stamp_mb(kda)
|
50
|
+
d = stamp_descricao(kda)
|
51
|
+
return if d.empty?
|
52
|
+
|
53
|
+
@base += '-' + I18n.transliterate(d, locale: :pt)
|
54
|
+
.gsub(/[ [[:punct:]]]/, '-')
|
65
55
|
end
|
66
56
|
|
67
|
-
|
68
|
-
|
57
|
+
# @param kda (see #final)
|
58
|
+
# @return [String] texto base do selo
|
59
|
+
def stamp_base(kda)
|
60
|
+
@base = id + '-' + stamp_rubrica(kda) + stamp_sha
|
69
61
|
end
|
70
62
|
|
71
|
-
|
63
|
+
# @param kda (see #final)
|
64
|
+
# @return [String] adiciona digitos do valor absoluto do documento
|
65
|
+
# @example kda-ft1901 (see #final)
|
66
|
+
def stamp_digitos(kda)
|
72
67
|
n = kda.inject(0) { |s, e| s + e[4].to_i }.abs
|
73
68
|
@base += '-' + format('%<valor>06d', valor: n)
|
74
69
|
end
|
75
70
|
|
76
|
-
|
77
|
-
|
71
|
+
# @param kda (see #final)
|
72
|
+
# @return [String] adiciona ids dos movimentos multibanco
|
73
|
+
# @example kda-ft1904
|
74
|
+
# [["ft1904-mb00016410","material","mangueira","limpeza","-3998"],
|
75
|
+
# ["ft1904-mb00095312","material","lampadas","sos","-4585"]]
|
76
|
+
def stamp_mb(kda)
|
77
|
+
d = kda.group_by { |e| e[0][/-(mb\d+)/, 1] }
|
78
78
|
.keys.join('-')
|
79
79
|
@base += '-' + d unless d.size.zero?
|
80
80
|
end
|
81
81
|
|
82
|
-
|
83
|
-
|
82
|
+
# @param kda (see #final)
|
83
|
+
# @return [String] descricoes dos movimentos contabilidade
|
84
|
+
# @example kda-rc1911
|
85
|
+
# [[_,_,"quota 2019-Janeiro","glB albino soares","541"],
|
86
|
+
# [_,_,"quota 2019-Fevereiro","glB albino soares","541"]]
|
87
|
+
# @example kda-ft1901 (see #final)
|
88
|
+
def stamp_descricao(kda)
|
89
|
+
if id[0] == 'f'
|
84
90
|
kda.group_by { |e| e[2] }
|
85
91
|
else
|
86
92
|
kda.group_by { |e| e[2][/\d{4}-(\w{3})/, 1] }
|
87
93
|
end.keys.filter { |e| e }.join('-')
|
88
94
|
end
|
89
95
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
numb_stamp(kda)
|
96
|
-
d = sfim_stamp(kda)
|
97
|
-
return if d.empty?
|
98
|
-
|
99
|
-
@base += '-' + I18n.transliterate(d, locale: :pt)
|
100
|
-
.gsub(/[ [[:punct:]]]/, '-')
|
101
|
-
end
|
102
|
-
|
103
|
-
def rubrica(kda)
|
96
|
+
# @param kda (see #final)
|
97
|
+
# @return [String] rubrica dos movimentos contabilidade
|
98
|
+
# @example kda-ft1901 (see #final)
|
99
|
+
# @example kda-rc1911 (see #stamp_descricao)
|
100
|
+
def stamp_rubrica(kda)
|
104
101
|
if kda
|
105
|
-
|
106
|
-
# isto permite fazer re-classificacoes de documentos
|
107
|
-
if key[0] == 'f'
|
102
|
+
if id[0] == 'f'
|
108
103
|
kda.group_by { |e| e[1] }
|
109
104
|
else
|
105
|
+
# rubrica recibos = id condomino (ex: h3d)
|
110
106
|
kda.group_by { |e| e[3][/\w+/] }
|
111
107
|
end.keys.join('-')
|
112
108
|
else
|
@@ -114,30 +110,51 @@ module Arquivo
|
|
114
110
|
end
|
115
111
|
end
|
116
112
|
|
117
|
-
|
113
|
+
# @return [String] SHA256 do documento para arquivar
|
114
|
+
def stamp_sha
|
118
115
|
'-' + `sha256sum #{file}`[/\w+/]
|
119
116
|
end
|
120
117
|
|
121
|
-
|
122
|
-
|
118
|
+
# @param [String] jpg imagem final (se existir)
|
119
|
+
# @return [Boolean] scanned pdf?
|
120
|
+
def jpg?(jpg)
|
121
|
+
return false if id[0] == 'r'
|
123
122
|
|
124
|
-
o = "tmp/#{
|
125
|
-
#
|
123
|
+
o = "tmp/#{id}.txt"
|
124
|
+
# se pdf contem texto -> not scanned
|
126
125
|
system "pdftotext -q -eol unix -nopgbrk \"#{file}\" #{o}"
|
127
|
-
File.size?(o)
|
128
|
-
|
126
|
+
return false if File.size?(o)
|
127
|
+
|
128
|
+
system "pdfimages -q -j \"#{file}\" tmp/#{id}"
|
129
|
+
# utilizar somente 1 imagem, comvertida em jpg
|
130
|
+
system "convert #{Dir.glob("tmp/#{id}-???.???")[0]} #{jpg} #{O2}"
|
129
131
|
|
130
|
-
|
131
|
-
|
132
|
+
File.size?(jpg) > LT
|
133
|
+
end
|
132
134
|
|
133
|
-
|
134
|
-
|
135
|
-
#
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
# cria pdf com selo no canto inferior esquerdo
|
136
|
+
def marca
|
137
|
+
# nome pdf com selo determina a ordem das paginas no arquivo final
|
138
|
+
o = "tmp/stamped-#{base[/-(\w+)/, 1]}-#{id}.pdf"
|
139
|
+
s = '2 2 moveto /Ubuntu findfont 7 scalefont ' \
|
140
|
+
"setfont (#{base}) show"
|
141
|
+
system "#{ghostscript} -sOutputFile=tmp/stamp-#{id}.pdf -c \"#{s}\";" \
|
142
|
+
"pdftk tmp/zip/#{base}.pdf " \
|
143
|
+
"stamp tmp/stamp-#{id}.pdf output #{o} #{O2}"
|
144
|
+
end
|
139
145
|
|
140
|
-
|
146
|
+
# @return [String] comando PDF language interpreter c118
|
147
|
+
def ghostscript
|
148
|
+
# filtrar images para scq e extratos
|
149
|
+
fi = /^[se]/i.match?(id) ? ' -dFILTERIMAGE' : ''
|
150
|
+
|
151
|
+
'gs -sDEVICE=pdfwrite ' \
|
152
|
+
'-dNOPAUSE -dBATCH -dQUIET ' \
|
153
|
+
'-sPAPERSIZE=a4 -dFIXEDMEDIA -dPDFFitPage ' \
|
154
|
+
'-dPDFSETTINGS=/screen -dDetectDuplicateImages ' \
|
155
|
+
'-dColorImageDownsampleThreshold=1 ' \
|
156
|
+
'-dGrayImageDownsampleThreshold=1 ' \
|
157
|
+
'-dMonoImageDownsampleThreshold=1' + fi
|
141
158
|
end
|
142
159
|
end
|
143
160
|
end
|
data/lib/arquivo/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arquivo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hernâni Rodrigues Vaz
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-12-
|
11
|
+
date: 2019-12-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,7 +94,23 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0.1'
|
97
|
-
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.9'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.9'
|
111
|
+
description: Processa documentos do condominio ph1341c118 para arquivo. Pode tambem
|
112
|
+
segmentar PDFs e MINUTAS. Tendo os documentos em pastas separadas, pode ainda criar
|
113
|
+
arquivos apropriados.
|
98
114
|
email:
|
99
115
|
- hernanirvaz@gmail.com
|
100
116
|
executables:
|
@@ -144,5 +160,5 @@ requirements: []
|
|
144
160
|
rubygems_version: 3.0.3
|
145
161
|
signing_key:
|
146
162
|
specification_version: 4
|
147
|
-
summary:
|
163
|
+
summary: Processa documentos do condominio ph1341c118 para arquivo.
|
148
164
|
test_files: []
|