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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db2e214ab2f4424ed534529d6601643e05256b201c75ac4d6ce13ce63167f62e
4
- data.tar.gz: 657d0d3b58f42150e1930d669444ab71a019df9e2b006abc2ac75caee8e4a1ee
3
+ metadata.gz: ed65dd3f514e1950093d139e190ba9104037dc1637f4aa63a879aa5a26e7707f
4
+ data.tar.gz: 75603c49ac01b58575ad63bba4aee0cf5265594fa41a95407aaa10125a717cf4
5
5
  SHA512:
6
- metadata.gz: 7aa8742696b7e53e4af0ff69c8c85b71fa48969751d96f162c8546d276373796c2c0dba1a075d5d00fec89ed24bda847083e1061d7698d4919e1b0815211c91c
7
- data.tar.gz: 074e49e12a77c243ff62b32bde04b6d72e84576da5f152db4e3c85b643b245cca8f59058a010ea6318c92aed10f2776d339fb5a047e52ab8614f560aa19d00d4
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.1)
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.1)
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 dir PASTA # processa faturas/recibos/extratos/minutas
24
- $ arquivo pdf FILE # processa extratos ou faturas
25
- $ arquivo help [COMMAND] # Describe available commands or one specific command
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 = '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'
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 = 'Write a short summary, because RubyGems requires one.'
15
- spec.description = ' Write a longer description or delete this line.'
16
- spec.license = 'MIT'
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 para analisar/processar documentos c118
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
- def mp3(file)
20
- return unless File.exist?(file) && File.ftype(file) == 'file'
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(file)
23
- return unless f.processa_minuta?
30
+ f = C118mp3.new(minuta, options)
31
+ return unless f.segmenta_minuta?
24
32
 
25
33
  system "mkdir -p #{f.base}"
26
- f.processa_minuta(options)
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
- def pdf(file)
32
- return unless File.exist?(file) && File.ftype(file) == 'file'
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(file)
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.extrato?
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
- ' e cria arquivos c118'
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 :noise, type: :boolean, default: false,
54
- desc: 'ruido de fundo - sim ou nao'
55
- option :sound, type: :numeric, default: 1.0,
56
- desc: 'minimo som que determina fim do silencio (segundos)'
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
- def dir(fdir)
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
- FT = ['.mp3', '.m4a', '.wav', '.sox'].freeze
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
- # analisar/processar pasta
16
+ # permite processar e arquivar pasta com documentos c118
15
17
  class C118dir < Enumerator
16
- # @return [Enumerator] items dentro duma pasta
17
- attr_reader :items
18
- # @return [String] documento c118
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, options)
24
+ C118dir.new(item, opcoes).processa_pasta(item)
43
25
  else
44
- processa_file(options, File.extname(item).downcase)
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;rm -rf tmp"
45
+ ";cd ../..;gzip --best #{base}.tar" \
46
+ '' # ';rm -rf tmp'
62
47
 
63
48
  puts "#{base} (#{num})"
64
49
  end
65
50
 
66
- def processa_file(options, ext)
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(options, dados)
69
- when '.pdf' then C118pdf.new(item).processa_pdf(options, dados)
70
- when *FT
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: #{item} so posso processar"
61
+ puts "erro: #{ext} nao posso processar este tipo de dicheiro"
74
62
  end
75
63
  end
76
64
 
77
- # @return [String] ficheiro dentro da pasta
78
- def next_item
79
- @item = items.next
80
- rescue StopIteration
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
- obtem_noiseprof(pasta, options)
71
+ obtem_dados(pasta)
72
+ obtem_noiseprof(pasta)
92
73
  end
93
- processa_items(options)
74
+ processa_items
94
75
  end
95
76
 
96
- def obtem_dados(dir)
97
- return unless /fac?tura/i.match?(dir) || /recibo/i.match?(dir)
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
- # obtem dados (faturas/recibos) da sheet c118-contas
100
- id = '1PbiMrtTtqGztZMhe3AiJbDS6NQE9o3hXebnQEFdt954'
101
- sh = (/fac?tura/i.match?(dir) ? 'rft' : 'rrc') + '!A2:E'
102
- @dados = c118_sheets.get_spreadsheet_values(id, sh).values
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 arquivo de credencias
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
@@ -3,64 +3,82 @@
3
3
  require 'pdf-reader'
4
4
 
5
5
  module Arquivo
6
- # analisar/processar pdf
6
+ # permite processar documentos PDF
7
7
  class C118pdf < String
8
- def c118_gs
9
- # filtrar images para scq e extratos
10
- fi = /^[se]/i.match?(key) ? ' -dFILTERIMAGE' : ''
11
-
12
- 'gs -sDEVICE=pdfwrite ' \
13
- '-dNOPAUSE -dBATCH -dQUIET ' \
14
- '-sPAPERSIZE=a4 -dFIXEDMEDIA -dPDFFitPage ' \
15
- '-dPDFSETTINGS=/screen -dDetectDuplicateImages ' \
16
- '-dColorImageDownsampleThreshold=1 ' \
17
- '-dGrayImageDownsampleThreshold=1 ' \
18
- '-dMonoImageDownsampleThreshold=1' + fi
19
- end
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
- def processa_extrato?
22
- return true if ext == '.pdf' &&
23
- size.positive? &&
24
- !File.exist?(base) &&
25
- first_extrato?
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
- if File.exist?(base)
28
- puts "erro: #{base} pasta ja existe"
29
- else
30
- puts "erro: #{file} nao consigo obter primeira pagina do EXTRATO"
31
- end
32
- false
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 conta_c118?
47
+ @paginas << cnt if pagina_extrato?
38
48
  if proxima_pagina
39
- faz_extrato if extrato?
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
- def extrato?
47
- conta_c118? && pagina.match?(/extrato +combinado/i)
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
- def faz_extrato
51
- system "#{c118_gs} " \
52
- "-sOutputFile=#{base}/#{nome}-extrato.pdf " \
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
- def conta_c118?
74
+ # @return [Boolean] pagina de extrato?
75
+ def pagina_extrato?
59
76
  pagina.include?('45463760224')
60
77
  end
61
78
 
62
- def first_extrato?
63
- leitor && proxima_pagina && proximo_extrato
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
- def proximo_extrato
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].to_s[/\d{2}$/]}#{n[1]}"
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
- # analisar/processar jpg
18
+ # permite processar documentos em imagens JPG
19
19
  class C118jpg < String
20
- # @return [String] nome do ficheiro
20
+ # @return [String] nome do documento
21
21
  attr_reader :file
22
- # @return [String] extensao do ficheiro
22
+ # @return [String] extensao do documento
23
23
  attr_reader :ext
24
- # @return [String] base do ficheiro
24
+ # @return [String] base do documento
25
25
  attr_reader :base
26
- # @return [String] key do documento ft????/rc????/ex??0??/sc??????
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
- # @return [C118jpg] jpg c118
32
- def initialize(fjpg)
33
- @file = fjpg
34
- @ext = File.extname(fjpg).downcase
35
- @base = File.basename(fjpg, File.extname(fjpg))
36
- @key = @base[/\w+/]
37
- @size = File.size(fjpg)
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
- def processa_jpg(options, dados)
41
- trim(options).converte(options).final(dados[key]).marca
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
- def parm_trim(options, fuzz)
45
- "-fuzz #{fuzz}% -trim +repage #{parm_qualidade(options)} " \
46
- "tmp/#{key}-#{fuzz}.jpg #{O2}"
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
- # obter jpg menor triming borders ao maximo
61
+ # aparar borders ao maximo
57
62
  while f >= 1
58
- system "convert \"#{file}\" #{parm_trim(options, f)}"
59
- h[f] = File.size("tmp/#{key}-#{f}.jpg")
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("tmp/#{key}-#{m[0]}.jpg") : self
68
+ m[1].between?(LT, size) ? C118jpg.new(m[0], opcoes) : self
64
69
  end
65
70
 
66
- def converte(options)
67
- # expande jpg on a larger canvas
68
- system "convert \"#{file}\" #{expande} #{parm_qualidade(options)} " \
69
- "-format pdf tmp/#{key}-trimed.pdf #{O2}"
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
- # devolve pdf processado a partir de jpg
72
- C118pdf.new("tmp/#{key}-trimed.pdf")
88
+ # @return [String] opcoes comprimir jpg
89
+ def oqualidade
90
+ "-quality #{opcoes[:quality]}% -compress jpeg"
73
91
  end
74
92
 
75
- def expande
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
- # analisar/processar mp3
4
+ # permite processar documentos em audio
5
5
  class C118mp3 < String
6
- # @return [String] nome do ficheiro
6
+ # @return [String] nome do documento
7
7
  attr_reader :file
8
- # @return [String] extensao do ficheiro
8
+ # @return [String] extensao do documento
9
9
  attr_reader :ext
10
- # @return [String] base do ficheiro
10
+ # @return [String] base do documento
11
11
  attr_reader :base
12
- # @return [Float] segundos do mp3
12
+ # @return [Integer] tamanho do documento
13
13
  attr_reader :size
14
+ # @return [Hash] opcoes parametrizar MINUTA
15
+ attr_reader :opcoes
14
16
 
15
- # @return [String] nome segmento
16
- attr_reader :nome
17
-
18
- # @return [C118mp3] mp3 c118
19
- def initialize(fmp3)
20
- @file = fmp3
21
- @ext = File.extname(fmp3).downcase
22
- @base = File.basename(fmp3, File.extname(fmp3))
23
- @size = `soxi -V0 -D #{fmp3} #{O1}`.to_f
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
- def processa_mp3(options, npr)
27
- cmd = if npr
28
- "noisered #{npr} #{format('%<v>.9f', v: options[:amount])} "
29
- else
30
- ''
31
- end
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
- def segmenta(tps, pse, cmd)
37
- return cmd[1..-1] unless pse < tps.size
38
-
39
- puts proximo_segmento(pse)
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
- cmd += ";sox #{file} #{nome} trim #{tps[pse]}"
42
- pse += 1
43
- cmd += " =#{tps[pse]}" if pse < tps.size
45
+ # @return [String] opcoes sample rate & channels
46
+ def orate
47
+ "rate -v #{opcoes[:rate]}k channels 1"
48
+ end
44
49
 
45
- segmenta(tps, pse, cmd + " #{O2}")
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
- def proximo_segmento(pse)
49
- out = "s#{format('%<v>02d', v: pse)}-#{base[/\d{8}/]}#{base[/-\w+/]}"
50
- @nome = "#{base}/#{out}#{ext}"
51
- out
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
- def processa_minuta(options)
55
- system segmenta(['0'] + options[:tempos], 0, '')
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
- def processa_minuta?
59
- return true if FT.include?(ext) && size.positive? &&
60
- !File.exist?(base)
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 #{FT}"
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
- # analisar/processar pasta
4
+ # permite processar e arquivar pasta com documentos c118
5
5
  class C118dir < Enumerator
6
- def obtem_noiseprof(pasta, options)
7
- return unless /minuta/i.match?(pasta) && !options[:noise]
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
- d = Dir.glob(File.join(pasta, '*')).map { |e| [e, duracao(e)] }
10
- t = 0
11
- s = ['', 0, 0]
12
- s = maximo(d, t += 1, options[:sound]) while t < 9 && s[2] <= silence
15
+ # @return (see #obtem_dados)
16
+ attr_reader :dados
17
+ # @return (see #obtem_noiseprof)
18
+ attr_reader :noiseprof
13
19
 
14
- processa_silencio(s)
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
- def processa_silencio(seg)
18
- return unless seg[2] > silence
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
- o = "tmp/silencio-#{File.basename(seg[0])}"
21
- system "sox #{seg[0]} #{o} trim 0 #{seg[2]} #{O2}"
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
- processa_noiseprof(seg, o)
26
- end
49
+ t = -1
50
+ m = ['', 0]
51
+ m = obtem_maximo_silencio(l, t += 1) while noisy?(m, t)
27
52
 
28
- def processa_noiseprof(seg, trm)
29
- o = "tmp/noiseprof-#{File.basename(seg[0], File.extname(seg[0]))}"
30
- # obter noiseprof do silencio encontrado
31
- system "sox #{trm} -n noiseprof #{o} #{O2}"
53
+ cria_noiseprof(m)
54
+ end
32
55
 
33
- # so noiseprof validos sao devolvidos
34
- @silence = File.size?(o) ? seg[2] : 0.0
35
- @noiseprof = silence.positive? ? o : nil
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
- def maximo(seg, thr, som)
39
- seg.sort.map { |e| add_silencio(e, thr, som) }.max_by { |_, _, s| s }
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
- def add_silencio(seg, thr, som)
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 #{som}t #{thr}% #{O2}"
45
- s = (seg[1] - duracao(o)).round(2, half: :down)
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
- seg + [s > som ? s : 0.0]
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
- def duracao(seg)
51
- `soxi -V0 -D #{seg} #{O1}`.to_f
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
- # analisar/processar pdf
9
+ # permite processar documentos PDF
10
10
  class C118pdf < String
11
- # @return [String] nome do documento
12
- attr_reader :file
13
- # @return [String] extensao do documento
14
- attr_reader :ext
15
- # @return [String] base do documento
16
- attr_reader :base
17
- # @return [String] key do documento ft????/rc????/ex??0??/sc??????
18
- attr_reader :key
19
- # @return [Integer] tamanho do pdf
20
- attr_reader :size
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
- (tpdf.size < size ? tpdf : self).final(dados[key]).marca
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
- c118_stamp(kda)
32
+ stamp(kda)
58
33
  o = "tmp/zip/#{base}.pdf"
59
34
 
60
- system "#{c118_gs} -sOutputFile=#{o} \"#{file}\" #{O2}"
61
- # usar copia do original se processado for maior
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
- def base_stamp(kda)
68
- @base = key + '-' + rubrica(kda) + digest
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
- def vnum_stamp(kda)
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
- def numb_stamp(kda)
77
- d = kda.group_by { |e| e[0][/-(mb\d{8})/, 1] }
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
- def sfim_stamp(kda)
83
- if key[0] == 'f'
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
- def c118_stamp(kda)
91
- base_stamp(kda)
92
- return unless kda
93
-
94
- vnum_stamp(kda)
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
- # rubrica obtida da sheet arquivo
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
- def digest
113
+ # @return [String] SHA256 do documento para arquivar
114
+ def stamp_sha
118
115
  '-' + `sha256sum #{file}`[/\w+/]
119
116
  end
120
117
 
121
- def jpg?
122
- return false if key[0] == 'r'
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/#{key}.txt"
125
- # teste scanned pdf (se contem texto -> not scanned)
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) ? false : true
128
- end
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
- def extract
131
- o = "tmp/#{key}-extract.jpg"
132
+ File.size?(jpg) > LT
133
+ end
132
134
 
133
- system "pdfimages -q -j \"#{file}\" tmp/#{key}"
134
- # nem sempre as imagens sao jpg
135
- # somente utilizar a primeira
136
- g = Dir.glob("tmp/#{key}-???.???")
137
- system "convert #{g[0]} #{o} #{O2}"
138
- return unless File.size(o) > LT
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
- C118jpg.new(o)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arquivo
4
- VERSION = '0.2.1'
4
+ VERSION = '0.2.2'
5
5
  end
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.1
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-23 00:00:00.000000000 Z
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
- description: " Write a longer description or delete this line."
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: Write a short summary, because RubyGems requires one.
163
+ summary: Processa documentos do condominio ph1341c118 para arquivo.
148
164
  test_files: []