dragonfly_pdf 0.0.2 → 0.0.3

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
  SHA1:
3
- metadata.gz: 5e969fb6354ce699bbdad681ffacfbfd465ad47a
4
- data.tar.gz: 975403373d406f38f528fce2a0c411adf58c9d4b
3
+ metadata.gz: 2f7513fd9507eb6bf49ad4a51d4204264df9c758
4
+ data.tar.gz: 235b85f9685ea39fa14dd0e7d25edc876b66142d
5
5
  SHA512:
6
- metadata.gz: a5a235d8b1bef8fad59fe522472f674c93636cddfff4b6f24dfbe4b40210aec31d2fa4ef674be22eff4c41153f3fa241b43a6f94c65f8ae9c936924ed2f9de5b
7
- data.tar.gz: ec751387a15c6110c006c1680be1ecf255698865d1e5e6de8d5146b4e13549aee4714366e9da51041aeaedda0dc199d51ff02d253d0611ae0615f9fd5e1f1306
6
+ metadata.gz: 6ceffc982e0b65454b263ce97e81aaac26104ac791026184020d2b4b8b9f012cf7dbad491bdc919ea85df685d64450a3193222b95896eea66e7f48214e485936
7
+ data.tar.gz: a18b986d6de46ba7ae00072da65b884dbf541dfdac594d2cfa453676236fecae4423304dccb208b4db28a987ce2ea4738bbbc72c92f37735a1b58d1224d2dd91
data/README.md CHANGED
@@ -23,7 +23,7 @@ Or install it yourself as:
23
23
 
24
24
  $ gem install dragonfly_pdf
25
25
 
26
- ## Plugin
26
+ ## Usage
27
27
  The analyser and processors are added by configuring the plugin
28
28
 
29
29
  ```ruby
@@ -32,25 +32,25 @@ Dragonfly.app.configure do
32
32
  end
33
33
  ```
34
34
 
35
- ## Analysers
36
-
37
- ### PDF properties
35
+ ### Spreads
38
36
 
39
- This processor has argument `spreads` (by default set to `false`). When `true`, the analyser assumes the PDF might contain 2 real pages per one PDF page (as when saving with the spreads option in InDesign) and recalculates the PDF properties accordingly (including situations when PDF starts or ends with single page).
37
+ PDFs that contain spreads (as when saving with the spreads option in InDesign) require setting the `spreads` [metadata](http://markevans.github.io/dragonfly/models/#meta-data) attribute to `true`:
40
38
 
41
39
  ```ruby
42
- pdf.pdf_properties(opts={})
40
+ pdf.metadata['spreads'] = true
43
41
  ```
44
42
 
45
- The available options and their default values are:
43
+ ## Analysers
44
+
45
+ ### PDF properties
46
+
47
+ Reads properties from a PDF.
46
48
 
47
49
  ```ruby
48
- {
49
- spreads: false
50
- }
50
+ pdf.pdf_properties
51
51
  ```
52
52
 
53
- Returns Hash of PDF properties:
53
+ It returns a hash of properties:
54
54
 
55
55
  ```ruby
56
56
  {
@@ -64,7 +64,7 @@ Returns Hash of PDF properties:
64
64
  }
65
65
  ```
66
66
 
67
- When the `spreads` argument is set to true, all page arrays (page_numbers, widths, heights, aspect_ratios) are two dimensional (as illustrated above), representing spreads and nested individual pages.
67
+ When the `spreads` metadata is set to `true`, the analyser assumes the PDF contains 2 real pages per one PDF page and recalculates the PDF properties accordingly (including situations where the PDF begins or ends with a single page). All page arrays (`page_numbers`, `widths`, `heights`, `aspect_ratios`) are then two dimensional (as illustrated above), representing spreads and nested individual pages.
68
68
 
69
69
  ## Processors
70
70
 
@@ -73,15 +73,7 @@ When the `spreads` argument is set to true, all page arrays (page_numbers, width
73
73
  Extracts page from PDF.
74
74
 
75
75
  ```ruby
76
- pdf.page(page_number=1, opts={})
77
- ```
78
-
79
- The available options and their default values are:
80
-
81
- ```ruby
82
- {
83
- spreads: false
84
- }
76
+ pdf.page(page_number=1)
85
77
  ```
86
78
 
87
79
  ### Page Thumb
@@ -98,7 +90,6 @@ The available options and their default values are:
98
90
  {
99
91
  density: 600,
100
92
  format: :png,
101
- spreads: false
102
93
  }
103
94
  ```
104
95
 
@@ -108,4 +99,4 @@ The available options and their default values are:
108
99
  2. Create your feature branch (`git checkout -b my-new-feature`)
109
100
  3. Commit your changes (`git commit -am 'Add some feature'`)
110
101
  4. Push to the branch (`git push origin my-new-feature`)
111
- 5. Create a new Pull Request
102
+ 5. Create a new Pull Request
@@ -4,19 +4,19 @@ module DragonflyPdf
4
4
  module Analysers
5
5
  class PdfProperties
6
6
 
7
- def call content, opts={}
8
- spreads = opts.fetch(:spreads, false)
7
+ def call content
8
+ spreads = content.meta['spreads'] || false
9
9
 
10
10
  pdf = PDF::Reader.new(content.file)
11
11
 
12
12
  {
13
+ aspect_ratios: aspect_ratios(pdf, spreads),
14
+ heights: heights(pdf, spreads),
15
+ info: pdf.info,
13
16
  page_count: page_count(pdf, spreads),
14
- spread_count: spread_count(pdf, spreads),
15
17
  page_numbers: page_numbers(pdf, spreads),
16
- widths: widths(pdf, spreads),
17
- heights: heights(pdf, spreads),
18
- aspect_ratios: aspect_ratios(pdf, spreads),
19
- info: pdf.info
18
+ spread_count: spread_count(pdf, spreads),
19
+ widths: widths(pdf, spreads)
20
20
  }
21
21
  end
22
22
 
@@ -24,11 +24,11 @@ module DragonflyPdf
24
24
  app.add_analyser :widths do |content|
25
25
  content.analyse(:pdf_properties)[:widths]
26
26
  end
27
-
27
+
28
28
  app.add_analyser :heights do |content|
29
29
  content.analyse(:pdf_properties)[:heights]
30
30
  end
31
-
31
+
32
32
  app.add_analyser :aspect_ratios do |content|
33
33
  attrs = content.analyse(:pdf_properties)[:aspect_ratios]
34
34
  end
@@ -36,9 +36,9 @@ module DragonflyPdf
36
36
  app.add_analyser :info do |content|
37
37
  attrs = content.analyse(:pdf_properties)[:info]
38
38
  end
39
-
39
+
40
40
  # ---------------------------------------------------------------------
41
-
41
+
42
42
  app.add_processor :page_thumb, DragonflyPdf::Processors::PageThumb.new
43
43
  app.add_processor :page, DragonflyPdf::Processors::Page.new
44
44
  end
@@ -7,11 +7,11 @@ module DragonflyPdf
7
7
  class Page
8
8
 
9
9
  def call content, page_number=1, opts={}
10
- spreads = opts.fetch(:spreads, false)
10
+ spreads = content.meta['spreads'] || false
11
11
  pdf_page_number = page_number
12
12
  crop_args = ''
13
13
 
14
- pdf_properties = DragonflyPdf::Analysers::PdfProperties.new.call(content, spreads: spreads)
14
+ pdf_properties = DragonflyPdf::Analysers::PdfProperties.new.call(content)
15
15
 
16
16
  raise DragonflyPdf::PageNotFound unless pdf_properties[:page_numbers].flatten.include?(page_number)
17
17
 
@@ -7,12 +7,12 @@ module DragonflyPdf
7
7
  def call content, page_number=1, opts={}
8
8
  format = opts.fetch(:format, :png)
9
9
  density = opts.fetch(:density, 600)
10
- spreads = opts.fetch(:spreads, false)
10
+ spreads = content.meta['spreads'] || false
11
11
 
12
12
  args = "-alpha deactivate -background white -colorspace sRGB -density #{density}x#{density} -define pdf:use-cropbox=true -define pdf:use-trimbox=true"
13
13
  crop_args = ''
14
14
 
15
- pdf_properties = DragonflyPdf::Analysers::PdfProperties.new.call(content, spreads: spreads)
15
+ pdf_properties = DragonflyPdf::Analysers::PdfProperties.new.call(content)
16
16
 
17
17
  raise DragonflyPdf::PageNotFound unless pdf_properties[:page_numbers].flatten.include?(page_number)
18
18
 
@@ -35,7 +35,7 @@ module DragonflyPdf
35
35
  content.meta['format'] = format.to_s
36
36
  content.ext = format
37
37
  end
38
-
38
+
39
39
  def update_url attrs, args='', opts={}
40
40
  format = opts['format']
41
41
  attrs.ext = format if format
@@ -1,3 +1,3 @@
1
1
  module DragonflyPdf
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -13,6 +13,13 @@ module DragonflyPdf
13
13
  let(:spreads_cover) { app.fetch_file(SAMPLES_DIR.join('sample_spreads_cover.pdf')) }
14
14
  let(:spreads_cover_back) { app.fetch_file(SAMPLES_DIR.join('sample_spreads_cover_back.pdf')) }
15
15
 
16
+ before do
17
+ spreads.meta['spreads'] = true
18
+ spreads_cover.meta['spreads'] = true
19
+ spreads_back.meta['spreads'] = true
20
+ spreads_cover_back.meta['spreads'] = true
21
+ end
22
+
16
23
  describe 'call' do
17
24
  let(:pdf_properties) { analyser.call(single_pages) }
18
25
 
@@ -32,25 +39,25 @@ module DragonflyPdf
32
39
 
33
40
  describe 'for PDF with spreads' do
34
41
  it 'returns two-dimensional array' do
35
- analyser.call(spreads, spreads: true)[:page_numbers].must_equal [[1,2],[3,4],[5,6],[7,8]]
42
+ analyser.call(spreads)[:page_numbers].must_equal [[1,2],[3,4],[5,6],[7,8]]
36
43
  end
37
44
  end
38
45
 
39
46
  describe 'for PDF with spreads and a cover' do
40
47
  it 'returns two-dimensional array' do
41
- analyser.call(spreads_cover, spreads: true)[:page_numbers].must_equal [[1],[2,3],[4,5],[6,7],[8,9]]
48
+ analyser.call(spreads_cover)[:page_numbers].must_equal [[1],[2,3],[4,5],[6,7],[8,9]]
42
49
  end
43
50
  end
44
51
 
45
52
  describe 'for PDF with spreads and a back cover' do
46
53
  it 'returns two-dimensional array' do
47
- analyser.call(spreads_back, spreads: true)[:page_numbers].must_equal [[1,2],[3,4],[5,6],[7,8],[9]]
54
+ analyser.call(spreads_back)[:page_numbers].must_equal [[1,2],[3,4],[5,6],[7,8],[9]]
48
55
  end
49
56
  end
50
57
 
51
58
  describe 'for PDF with spreads and a front and a back cover' do
52
59
  it 'returns two-dimensional array' do
53
- analyser.call(spreads_cover_back, spreads: true)[:page_numbers].must_equal [[1],[2,3],[4,5],[6,7],[8,9],[10]]
60
+ analyser.call(spreads_cover_back)[:page_numbers].must_equal [[1],[2,3],[4,5],[6,7],[8,9],[10]]
54
61
  end
55
62
  end
56
63
  end
@@ -66,25 +73,25 @@ module DragonflyPdf
66
73
 
67
74
  describe 'for PDF with spreads' do
68
75
  it 'returns correct page count' do
69
- analyser.call(spreads, spreads: true)[:page_count].must_equal 8
76
+ analyser.call(spreads)[:page_count].must_equal 8
70
77
  end
71
78
  end
72
79
 
73
80
  describe 'for PDF with spreads and a cover' do
74
81
  it 'returns correct page count' do
75
- analyser.call(spreads_cover, spreads: true)[:page_count].must_equal 9
82
+ analyser.call(spreads_cover)[:page_count].must_equal 9
76
83
  end
77
84
  end
78
85
 
79
86
  describe 'for PDF with spreads and a back cover' do
80
87
  it 'returns correct page count' do
81
- analyser.call(spreads_back, spreads: true)[:page_count].must_equal 9
88
+ analyser.call(spreads_back)[:page_count].must_equal 9
82
89
  end
83
90
  end
84
91
 
85
92
  describe 'for PDF with spreads and a front and a back cover' do
86
93
  it 'returns correct page count' do
87
- analyser.call(spreads_cover_back, spreads: true)[:page_count].must_equal 10
94
+ analyser.call(spreads_cover_back)[:page_count].must_equal 10
88
95
  end
89
96
  end
90
97
  end
@@ -100,25 +107,25 @@ module DragonflyPdf
100
107
 
101
108
  describe 'for PDF with spreads' do
102
109
  it 'returns correct page count' do
103
- analyser.call(spreads, spreads: true)[:spread_count].must_equal 4
110
+ analyser.call(spreads)[:spread_count].must_equal 4
104
111
  end
105
112
  end
106
113
 
107
114
  describe 'for PDF with spreads and a cover' do
108
115
  it 'returns correct page count' do
109
- analyser.call(spreads_cover, spreads: true)[:spread_count].must_equal 5
116
+ analyser.call(spreads_cover)[:spread_count].must_equal 5
110
117
  end
111
118
  end
112
119
 
113
120
  describe 'for PDF with spreads and a back cover' do
114
121
  it 'returns correct page count' do
115
- analyser.call(spreads_back, spreads: true)[:spread_count].must_equal 5
122
+ analyser.call(spreads_back)[:spread_count].must_equal 5
116
123
  end
117
124
  end
118
125
 
119
126
  describe 'for PDF with spreads and a front and a back cover' do
120
127
  it 'returns correct page count' do
121
- analyser.call(spreads_cover_back, spreads: true)[:spread_count].must_equal 6
128
+ analyser.call(spreads_cover_back)[:spread_count].must_equal 6
122
129
  end
123
130
  end
124
131
  end
@@ -134,25 +141,25 @@ module DragonflyPdf
134
141
 
135
142
  describe 'for PDF with spreads' do
136
143
  it 'returns widths' do
137
- analyser.call(spreads, spreads: true)[:widths].must_equal [[210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0]]
144
+ analyser.call(spreads)[:widths].must_equal [[210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0]]
138
145
  end
139
146
  end
140
147
 
141
148
  describe 'for PDF with spreads and a cover' do
142
149
  it 'returns correct widths' do
143
- analyser.call(spreads_cover, spreads: true)[:widths].must_equal [[210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0]]
150
+ analyser.call(spreads_cover)[:widths].must_equal [[210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0]]
144
151
  end
145
152
  end
146
153
 
147
154
  describe 'for PDF with spreads and a back cover' do
148
155
  it 'returns correct widths' do
149
- analyser.call(spreads_back, spreads: true)[:widths].must_equal [[210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0]]
156
+ analyser.call(spreads_back)[:widths].must_equal [[210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0]]
150
157
  end
151
158
  end
152
159
 
153
160
  describe 'for PDF with spreads and a front and a back cover' do
154
161
  it 'returns correct widths' do
155
- analyser.call(spreads_cover_back, spreads: true)[:widths].must_equal [[210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0]]
162
+ analyser.call(spreads_cover_back)[:widths].must_equal [[210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0, 210.0], [210.0]]
156
163
  end
157
164
  end
158
165
  end
@@ -168,25 +175,25 @@ module DragonflyPdf
168
175
 
169
176
  describe 'for PDF with spreads' do
170
177
  it 'returns heights' do
171
- analyser.call(spreads, spreads: true)[:heights].must_equal [[297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0]]
178
+ analyser.call(spreads)[:heights].must_equal [[297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0]]
172
179
  end
173
180
  end
174
181
 
175
182
  describe 'for PDF with spreads and a cover' do
176
183
  it 'returns correct heights' do
177
- analyser.call(spreads_cover, spreads: true)[:heights].must_equal [[297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0]]
184
+ analyser.call(spreads_cover)[:heights].must_equal [[297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0]]
178
185
  end
179
186
  end
180
187
 
181
188
  describe 'for PDF with spreads and a back cover' do
182
189
  it 'returns correct heights' do
183
- analyser.call(spreads_back, spreads: true)[:heights].must_equal [[297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0]]
190
+ analyser.call(spreads_back)[:heights].must_equal [[297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0]]
184
191
  end
185
192
  end
186
193
 
187
194
  describe 'for PDF with spreads and a front and a back cover' do
188
195
  it 'returns correct heights' do
189
- analyser.call(spreads_cover_back, spreads: true)[:heights].must_equal [[297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0]]
196
+ analyser.call(spreads_cover_back)[:heights].must_equal [[297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0, 297.0], [297.0]]
190
197
  end
191
198
  end
192
199
  end
@@ -202,25 +209,25 @@ module DragonflyPdf
202
209
 
203
210
  describe 'for PDF with spreads' do
204
211
  it 'returns aspect ratios' do
205
- analyser.call(spreads, spreads: true)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71]]
212
+ analyser.call(spreads)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71]]
206
213
  end
207
214
  end
208
215
 
209
216
  describe 'for PDF with spreads and a cover' do
210
217
  it 'returns correct aspect ratios' do
211
- analyser.call(spreads_cover, spreads: true)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71]]
218
+ analyser.call(spreads_cover)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71]]
212
219
  end
213
220
  end
214
221
 
215
222
  describe 'for PDF with spreads and a back cover' do
216
223
  it 'returns correct aspect ratios' do
217
- analyser.call(spreads_back, spreads: true)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71]]
224
+ analyser.call(spreads_back)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71]]
218
225
  end
219
226
  end
220
227
 
221
228
  describe 'for PDF with spreads and a front and a back cover' do
222
229
  it 'returns correct aspect ratios' do
223
- analyser.call(spreads_cover_back, spreads: true)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71]]
230
+ analyser.call(spreads_cover_back)[:aspect_ratios].map{ |i| i.is_a?(Array) ? i.map{ |j| j.round(2) } : i.round(2) }.must_equal [[0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71, 0.71], [0.71]]
224
231
  end
225
232
  end
226
233
  end
@@ -9,6 +9,12 @@ module DragonflyPdf
9
9
  # ---------------------------------------------------------------------
10
10
 
11
11
  describe 'analysers' do
12
+ it 'adds #pdf_properties' do
13
+ pdf.must_respond_to :pdf_properties
14
+ end
15
+ it 'allows an options parameter on #pdf_properties' do
16
+ pdf.pdf_properties.must_be_kind_of Hash
17
+ end
12
18
  it 'adds #page_count' do
13
19
  pdf.must_respond_to :page_count
14
20
  end
@@ -32,9 +38,8 @@ module DragonflyPdf
32
38
  end
33
39
  end
34
40
 
35
-
36
41
  # ---------------------------------------------------------------------
37
-
42
+
38
43
  describe 'processors' do
39
44
  it 'adds #page' do
40
45
  pdf.must_respond_to :page
@@ -14,6 +14,13 @@ module DragonflyPdf
14
14
  let(:spreads_cover) { Dragonfly::Content.new(app, SAMPLES_DIR.join('sample_spreads_cover.pdf')) }
15
15
  let(:spreads_cover_back) { Dragonfly::Content.new(app, SAMPLES_DIR.join('sample_spreads_cover_back.pdf')) }
16
16
 
17
+ before do
18
+ spreads.meta['spreads'] = true
19
+ spreads_cover.meta['spreads'] = true
20
+ spreads_back.meta['spreads'] = true
21
+ spreads_cover_back.meta['spreads'] = true
22
+ end
23
+
17
24
  # =====================================================================
18
25
 
19
26
  it 'returns PDF by default' do
@@ -14,6 +14,15 @@ module DragonflyPdf
14
14
  let(:spreads_cover) { Dragonfly::Content.new(app, SAMPLES_DIR.join('sample_spreads_cover.pdf')) }
15
15
  let(:spreads_cover_back) { Dragonfly::Content.new(app, SAMPLES_DIR.join('sample_spreads_cover_back.pdf')) }
16
16
 
17
+ before do
18
+ spreads.meta['spreads'] = true
19
+ spreads_cover.meta['spreads'] = true
20
+ spreads_back.meta['spreads'] = true
21
+ spreads_cover_back.meta['spreads'] = true
22
+ end
23
+
24
+ # =====================================================================
25
+
17
26
  it 'returns PNG by default' do
18
27
  processor.call(single_pages, 1, density: 72)
19
28
  get_mime_type(single_pages.path).must_include "image/png"
@@ -62,7 +71,7 @@ module DragonflyPdf
62
71
  end
63
72
 
64
73
  # ---------------------------------------------------------------------
65
-
74
+
66
75
  def get_mime_type file_path
67
76
  `file --mime-type #{file_path}`.gsub(/\n/, "")
68
77
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dragonfly_pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomas Celizna
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-02 00:00:00.000000000 Z
11
+ date: 2015-01-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dragonfly