File: images_spec.rb

package info (click to toggle)
ruby-prawn 2.4.0%2Bdfsg-1~
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, sid, trixie
  • size: 4,396 kB
  • sloc: ruby: 16,090; sh: 43; makefile: 20
file content (231 lines) | stat: -rw-r--r-- 7,473 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# frozen_string_literal: true

require 'spec_helper'
require 'set'
require 'pathname'

describe Prawn::Images do
  let(:pdf) { create_pdf }
  let(:filename) { "#{Prawn::DATADIR}/images/pigs.jpg" }

  it "onlies embed an image once, even if it's added multiple times" do
    pdf.image filename, at: [100, 100]
    pdf.image filename, at: [300, 300]

    output = pdf.render
    images = PDF::Inspector::XObject.analyze(output)
    # there should be 2 images in the page resources
    expect(images.page_xobjects.first.size).to eq(2)
    # but only 1 image xobject
    expect(output.scan(%r{/Type /XObject}).size).to eq(1)
  end

  it 'returns the image info object' do
    info = pdf.image(filename)

    expect(info).to be_a_kind_of(Prawn::Images::JPG)

    expect(info.height).to eq(453)
  end

  it 'accepts IO objects' do
    info =
      File.open(filename, 'rb') do |file|
        pdf.image(file)
      end

    expect(info.height).to eq(453)
  end

  it 'rewinds IO objects to be able to embed them multiply' do
    info =
      File.open(filename, 'rb') do |file|
        pdf.image(file)
        pdf.image(file)
      end

    expect(info.height).to eq(453)
  end

  it 'does not close passed-in IO objects' do
    File.open(filename, 'rb') do |file|
      pdf.image(file)

      expect(file).to_not be_closed
    end
  end

  it 'accepts Pathname objects' do
    info = pdf.image(Pathname.new(filename))
    expect(info.height).to eq(453)
  end

  describe 'closes opened files again after getting Pathnames', issue: 975 do
    describe 'spec with File message spy' do
      let(:not_filename) { 'non-existent filename' }
      let(:pathname_double) { instance_double('Pathname', file?: true) }

      before do
        file_content = File.new(filename, 'rb').read
        allow(Pathname).to receive(:new).with(not_filename) { pathname_double }
        allow(pathname_double).to receive(:binread) { file_content }
      end

      it 'uses only binread, which closes opened files' do
        # this implicitly tests that a file handle is closed again
        # because only stubbed binread can be called on not_filename
        _info = pdf.image(not_filename)
        expect(pathname_double).to have_received(:binread)
      end
    end

    system_has_lsof = system('lsof -v > /dev/null 2>&1')
    system_has_grep = system('grep --version > /dev/null 2>&1')
    if system_has_lsof && system_has_grep
      it 'closes opened files, spec with lsof' do
        gc_was_disabled = GC.disable # GC of File would close the file
        open_before = `lsof -c ruby | grep "#{filename}"`
        _info = pdf.image(Pathname.new(filename))
        open_after = `lsof -c ruby | grep "#{filename}"`
        GC.enable unless gc_was_disabled
        expect(open_after).to eq(open_before)
      end
    end

    if RUBY_PLATFORM != 'java'
      it 'closes opened files, spec with ObjectSpace' do
        gc_was_disabled = GC.disable # GC of File would close the file
        open_before = ObjectSpace.each_object(File).count { |f| !f.closed? }
        _info = pdf.image(Pathname.new(filename))
        open_after = ObjectSpace.each_object(File).count { |f| !f.closed? }
        GC.enable unless gc_was_disabled
        expect(open_after).to eq(open_before)
      end
    end
  end

  describe 'setting the length of the bytestream' do
    it 'correctlies work with images from Pathname objects' do
      pdf.image(Pathname.new(filename))
      expect(pdf).to have_parseable_xobjects
    end

    it 'correctlies work with images from IO objects' do
      pdf.image(File.open(filename, 'rb'))
      expect(pdf).to have_parseable_xobjects
    end

    it 'correctlies work with images from IO objects not set to mode rb' do
      pdf.image(File.open(filename, 'r'))
      expect(pdf).to have_parseable_xobjects
    end
  end

  it 'raise_errors an UnsupportedImageType if passed a BMP' do
    pending "spec disabled since tru256.bmp removed from source package (Debian patch)
    filename = "#{Prawn::DATADIR}/images/tru256.bmp"
    expect { pdf.image filename, at: [100, 100] }
      .to raise_error(Prawn::Errors::UnsupportedImageType)
  end

  it 'raise_errors an UnsupportedImageType if passed an interlaced PNG' do
    pending "spec disabled since dice_interlaced.png removed from source package (Debian patch)"
    filename = "#{Prawn::DATADIR}/images/dice_interlaced.png"
    expect { pdf.image filename, at: [100, 100] }
      .to raise_error(Prawn::Errors::UnsupportedImageType)
  end

  xit 'bumps PDF version to 1.5 or greater on embedding 16-bit PNGs' do
    pdf.image "#{Prawn::DATADIR}/images/16bit.png"
    expect(pdf.state.version).to be >= 1.5
  end

  xit 'embeds 16-bit alpha channels for 16-bit PNGs' do
    pdf.image "#{Prawn::DATADIR}/images/16bit.png"

    output = pdf.render
    expect(output).to match(%r{/BitsPerComponent 16})
    expect(output).to_not match(%r{/BitsPerComponent 8})
  end

  it 'flows an image to a new page if it will not fit on a page' do
    pdf.image filename, fit: [600, 600]
    pdf.image filename, fit: [600, 600]
    output = StringIO.new(pdf.render, 'r+')
    hash = PDF::Reader::ObjectHash.new(output)
    pages = hash.values.find do |obj|
      obj.is_a?(Hash) && obj[:Type] == :Pages
    end[:Kids]
    expect(pages.size).to eq(2)
    expect(hash[pages[0]][:Resources][:XObject].keys).to eq([:I1])
    expect(hash[pages[1]][:Resources][:XObject].keys).to eq([:I2])
  end

  it 'does not flow an image to a new page if it will fit on one page' do
    pdf.image filename, fit: [400, 400]
    pdf.image filename, fit: [400, 400]
    output = StringIO.new(pdf.render, 'r+')
    hash = PDF::Reader::ObjectHash.new(output)
    pages = hash.values.find do |obj|
      obj.is_a?(Hash) && obj[:Type] == :Pages
    end[:Kids]
    expect(pages.size).to eq(1)
    expect(Set.new(hash[pages[0]][:Resources][:XObject].keys)).to eq(
      Set.new(%i[I1 I2])
    )
  end

  it 'does not start a new page just for a stretchy bounding box' do
    allow(pdf).to receive(:start_new_page)
    pdf.bounding_box([0, pdf.cursor], width: pdf.bounds.width) do
      pdf.image filename
    end
    expect(pdf).to_not have_received(:start_new_page)
  end

  describe ':fit option' do
    it 'fits inside the defined constraints' do
      info = pdf.image filename, fit: [100, 400]
      expect(info.scaled_width).to be <= 100
      expect(info.scaled_height).to be <= 400

      info = pdf.image filename, fit: [400, 100]
      expect(info.scaled_width).to be <= 400
      expect(info.scaled_height).to be <= 100

      info = pdf.image filename, fit: [604, 453]
      expect(info.scaled_width).to eq(604)
      expect(info.scaled_height).to eq(453)
    end

    it 'moves text position' do
      y = pdf.y
      pdf.image filename, fit: [100, 400]
      expect(pdf.y).to be < y
    end
  end

  describe ':at option' do
    it 'does not move text position' do
      y = pdf.y
      pdf.image filename, at: [100, 400]
      expect(pdf.y).to eq(y)
    end
  end

  describe ':width option without :height option' do
    it 'scales the width and height' do
      info = pdf.image filename, width: 225
      expect(info.scaled_height).to eq(168.75)
      expect(info.scaled_width).to eq(225.0)
    end
  end

  describe ':height option without :width option' do
    it 'scales the width and height' do
      info = pdf.image filename, height: 225
      expect(info.scaled_height).to eq(225.0)
      expect(info.scaled_width).to eq(300.0)
    end
  end
end