poppler 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,49 @@
1
+ =begin
2
+ extconf.rb for Ruby/Poppler extention library
3
+ =end
4
+
5
+ PACKAGE_NAME = "poppler"
6
+ PACKAGE_ID = "poppler-glib"
7
+
8
+ begin
9
+ require 'mkmf-gnome2'
10
+ USE_GLIB_GEM = true
11
+ TOPDIR = File.expand_path(File.dirname(__FILE__) )
12
+ SRCDIR = TOPDIR + '/src'
13
+ require 'glib2'
14
+ require 'gdk_pixbuf2'
15
+ require 'gtk2/base'
16
+
17
+ rescue LoadError => e
18
+ TOPDIR = File.expand_path(File.dirname(__FILE__) + '/..')
19
+ MKMF_GNOME2_DIR = TOPDIR + '/glib/src/lib'
20
+ SRCDIR = TOPDIR + '/poppler/src'
21
+
22
+ $LOAD_PATH.unshift MKMF_GNOME2_DIR
23
+
24
+ require 'mkmf-gnome2'
25
+ end
26
+
27
+ PKGConfig.have_package(PACKAGE_ID, 0, 8, 0) or exit 1
28
+ setup_win32(PACKAGE_NAME)
29
+
30
+ check_cairo
31
+
32
+ if USE_GLIB_GEM
33
+ path = File.expand_path(ENV['GEM_HOME'] + "/gems/glib2-#{GLib::BINDING_VERSION.join('.')}/src")
34
+ add_depend_package("glib2", path, '/')
35
+ path = File.expand_path(ENV['GEM_HOME'] + "/gems/gdkpixbuf-#{GLib::BINDING_VERSION.join('.')}/src")
36
+ add_depend_package("gdk_pixbuf2", path, '/')
37
+ path = File.expand_path(ENV['GEM_HOME'] + "/gems/gtk2-#{GLib::BINDING_VERSION.join('.')}/src")
38
+ add_depend_package("gtk2", path, '/')
39
+ else
40
+ add_depend_package("glib2", "glib/src", TOPDIR)
41
+ add_depend_package("gtk2", "gtk/src", TOPDIR)
42
+ add_depend_package("gdk_pixbuf2", "gdkpixbuf", TOPDIR)
43
+ end
44
+
45
+ make_version_header("POPPLER", PACKAGE_ID)
46
+
47
+ create_pkg_config_file("Ruby/Poppler", PACKAGE_ID)
48
+ create_makefile_at_srcdir(PACKAGE_NAME, SRCDIR, "-DRUBY_POPPLER_COMPILATION")
49
+ create_top_makefile
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'poppler'
4
+
5
+ input = ARGV.first
6
+ output = input.sub(/\.pdf$/, "-numbered.pdf")
7
+
8
+ doc = Poppler::Document.new(input)
9
+ first_page = doc[0]
10
+
11
+ Cairo::PDFSurface.new(output, *first_page.size) do |surface|
12
+ context = Cairo::Context.new(surface)
13
+
14
+ doc.each_with_index do |page, i|
15
+ width, height = page.size
16
+ surface.set_size(width, height)
17
+ context.save do
18
+ context.render_poppler_page(page)
19
+ end
20
+ context.save do
21
+ layout = context.create_pango_layout
22
+ layout.text = i.to_s
23
+ layout.width = width * Pango::SCALE
24
+ layout.alignment = Pango::Layout::ALIGN_CENTER
25
+ layout.font_description = Pango::FontDescription.new("Sans 288")
26
+ context.move_to(0, (height - layout.pixel_size[1]) / 2.0)
27
+ context.set_source_rgba(0.1, 0.1, 0.1, 0.5)
28
+ context.show_pango_layout(layout)
29
+ end
30
+ context.show_page
31
+ end
32
+ end
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tempfile'
4
+ require "poppler"
5
+
6
+ if ARGV.size < 2
7
+ puts "usage: #{$0} input.pdf output [scale_ratio] [rotate_angle]"
8
+ exit(-1)
9
+ end
10
+
11
+ input, output, scale, rotate = ARGV
12
+ scale = (scale || 1.0).to_f
13
+ rotate = (rotate || 0).to_i % 360
14
+
15
+ ext_name = File.extname(output)[1..-1]
16
+ if ext_name
17
+ output_type = ext_name.downcase
18
+ output_type = "jpeg" if /jpg/ =~ output_type
19
+ else
20
+ output_type = "png"
21
+ end
22
+
23
+ def compute_size(width, height, rotate)
24
+ width = width.to_f
25
+ height = height.to_f
26
+ radius = 0
27
+ unless rotate.zero?
28
+ radius = rotate / 180.0 * Math::PI
29
+ if (90 < rotate and rotate < 180) or
30
+ (270 < rotate and rotate < 360)
31
+ radius -= Math::PI / 2
32
+ end
33
+ end
34
+ inner_angle1 = Math.atan(width / height)
35
+ inner_angle2 = Math.atan(height / width)
36
+ diagonal = Math.sqrt(width ** 2 + height ** 2)
37
+
38
+ angle1 = radius + inner_angle1
39
+ angle2 = radius + inner_angle2
40
+
41
+ bottom1 = diagonal * Math.cos(angle1)
42
+ length1 = (bottom1 * Math.tan(angle1)).abs.to_i
43
+ bottom2 = diagonal * Math.cos(angle2)
44
+ length2 = (bottom2 * Math.tan(angle2)).abs.to_i
45
+
46
+ if (0 <= rotate and rotate <= 90) or
47
+ (180 <= rotate and rotate <= 270)
48
+ [length1, length2]
49
+ else
50
+ [length2, length1]
51
+ end
52
+ end
53
+
54
+ def to_pixbuf_with_cairo(input, scale, rotate)
55
+ doc = Poppler::Document.new(input)
56
+ page = doc[0]
57
+ width, height = page.size.collect {|x| x * scale}
58
+ surface_width, surface_height = compute_size(width, height, rotate)
59
+
60
+ surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32,
61
+ surface_width, surface_height)
62
+ cr = Cairo::Context.new(surface)
63
+
64
+ half_width = surface_width / 2.0
65
+ half_height = surface_height / 2.0
66
+ cr.translate(half_width, half_height)
67
+ cr.rotate(rotate / 180.0 * Math::PI)
68
+ cr.translate(-half_width, -half_height)
69
+
70
+ cr.translate((surface_width - width) / 2.0,
71
+ (surface_height - height) / 2.0)
72
+ cr.set_source_rgb(1, 1, 1)
73
+ cr.rectangle(0, 0, width, height)
74
+ cr.fill
75
+
76
+ cr.scale(scale, scale)
77
+ cr.render_poppler_page(page)
78
+ temp = Tempfile.new("pdf2")
79
+ cr.target.write_to_png(temp.path)
80
+ cr.target.finish
81
+ Gdk::Pixbuf.new(temp.path)
82
+ end
83
+
84
+ def to_pixbuf(input, scale, rotate)
85
+ doc = Poppler::Document.new(input)
86
+ page = doc[0]
87
+ width, height = page.size.collect {|x| x * scale}
88
+ pixbuf_width, pixbuf_height = compute_size(width, height, rotate)
89
+ pixbuf = Gdk::Pixbuf.new(Gdk::Pixbuf::COLORSPACE_RGB, true, 8,
90
+ pixbuf_width, pixbuf_height)
91
+ page.render(0, 0, width, height, scale, rotate, pixbuf)
92
+ pixbuf
93
+ end
94
+
95
+ if Poppler.cairo_available?
96
+ puts "using cairo..."
97
+ pixbuf = to_pixbuf_with_cairo(input, scale, rotate)
98
+ else
99
+ pixbuf = to_pixbuf(input, scale, rotate)
100
+ end
101
+
102
+ if pixbuf.nil?
103
+ puts "Is it a PDF file?: #{input}"
104
+ exit(-1)
105
+ end
106
+ puts "saving to #{output}(#{pixbuf.width}x#{pixbuf.height})..."
107
+ pixbuf.save(output, output_type)
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "poppler"
4
+
5
+ if ARGV.size < 1
6
+ puts "usage: #{$0} input.pdf"
7
+ exit(-1)
8
+ end
9
+
10
+ input, = ARGV
11
+
12
+ output = input.sub(/\..+$/, ".svg")
13
+ output = "#{output}.svg" if input == output
14
+
15
+ doc = Poppler::Document.new(input)
16
+
17
+ width, height = doc.pages[0].size
18
+ Cairo::SVGSurface.new(output, width, height) do |surface|
19
+ surface.restrict_to_version("1_2")
20
+
21
+ context = Cairo::Context.new(surface)
22
+ doc.each do |page|
23
+ page.render(context)
24
+ context.show_page
25
+ end
26
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "poppler"
4
+
5
+ if ARGV.size < 1
6
+ puts "usage: #{$0} input.pdf"
7
+ exit(-1)
8
+ end
9
+
10
+ input = ARGV.shift
11
+ input_uri = "file://#{File.expand_path(input)}"
12
+
13
+ doc = Poppler::Document.new(input_uri)
14
+ doc.each do |page|
15
+ puts page.get_text
16
+ end
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "poppler"
4
+
5
+ if ARGV.size != 2
6
+ puts "usage: #{$0} input.pdf output.pdf"
7
+ exit(-1)
8
+ end
9
+
10
+ input, output = ARGV
11
+
12
+ doc = Poppler::Document.new(input)
13
+
14
+ width, height = doc.pages[0].size
15
+ Cairo::PDFSurface.new(output, width / 2, height) do |surface|
16
+ context = Cairo::Context.new(surface)
17
+
18
+ doc.each do |page|
19
+ width, height = page.size
20
+ half_width = width / 2
21
+ context.save do
22
+ context.rectangle(0, 0, half_width, height)
23
+ context.clip
24
+ page.render(context)
25
+ context.show_page
26
+ end
27
+
28
+ context.save do
29
+ context.translate(-half_width, 0)
30
+ context.rectangle(half_width, 0, half_width, height)
31
+ context.clip
32
+ page.render(context)
33
+ context.show_page
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,108 @@
1
+ # Copyright(C) 2006-2009 Ruby-GNOME2 Project.
2
+ #
3
+ # This library is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU Lesser General Public License as published by
5
+ # the Free Software Foundation, either version 2.1 of the License.
6
+ #
7
+ # Foobar is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
+ # GNU Lesser General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU Lesser General Public License
13
+ # along with Foobar. If not, see <http://www.gnu.org/licenses/>.
14
+
15
+ require "tempfile"
16
+ require "date"
17
+ require "glib2"
18
+ require "gdk_pixbuf2"
19
+ begin
20
+ require "gtk2/base"
21
+ rescue LoadError
22
+ rescue
23
+ if defined?(Gtk::InitError) and $!.class == Gtk::InitError
24
+ # ignore
25
+ else
26
+ raise
27
+ end
28
+ end
29
+ begin
30
+ require "cairo"
31
+ rescue LoadError
32
+ end
33
+ require "poppler.so"
34
+
35
+ module Poppler
36
+ LOG_DOMAIN = "Poppler"
37
+
38
+ VERSION = version.split(".").collect {|x| x.to_i}
39
+
40
+ class Document
41
+ private
42
+ def pdf_data?(data)
43
+ /\A%PDF-1\.\d/ =~ data
44
+ end
45
+
46
+ def ensure_uri(uri)
47
+ if pdf_data?(uri)
48
+ @pdf = Tempfile.new("ruby-poppler-pdf")
49
+ @pdf.binmode
50
+ @pdf.print(uri)
51
+ @pdf.close
52
+ uri = @pdf.path
53
+ end
54
+
55
+ if GLib.path_is_absolute?(uri)
56
+ GLib.filename_to_uri(uri)
57
+ elsif /\A[a-zA-Z][a-zA-Z\d\-+.]*:/.match(uri)
58
+ uri
59
+ else
60
+ GLib.filename_to_uri(File.expand_path(uri))
61
+ end
62
+ end
63
+ end
64
+
65
+ if defined?(TextField)
66
+ class TextField
67
+ def multiline?
68
+ type == FormTextType::MULTILINE
69
+ end
70
+
71
+ def file_select?
72
+ type == FormTextType::FILE_SELECT
73
+ end
74
+
75
+ def normal?
76
+ type == FormTextType::NORMAL
77
+ end
78
+ end
79
+ end
80
+
81
+ if defined?(ChoiceField)
82
+ class ChoiceField
83
+ def combo?
84
+ type == FormChioceType::COMBO
85
+ end
86
+
87
+ def list?
88
+ type == FormChoiceType::LIST
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ if Poppler.cairo_available?
95
+ module Cairo
96
+ class Context
97
+ def render_poppler_page(page, *args, &block)
98
+ page.render(self, *args, &block)
99
+ end
100
+
101
+ def render_poppler_page_selection(page, *args, &block)
102
+ page.render_selection(self, *args, &block)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ GLib::Log.set_log_domain(Poppler::LOG_DOMAIN)
@@ -0,0 +1,222 @@
1
+ /* -*- c-file-style: "ruby"; indent-tabs-mode: nil -*- */
2
+ /**********************************************************************
3
+
4
+ rbpoppler-action.c -
5
+
6
+ Copyright (C) 2006-2008 Ruby-GNOME2 Project Team
7
+
8
+ **********************************************************************/
9
+
10
+ #include "rbpoppler-private.h"
11
+
12
+ #define RVAL2DEST(obj) ((PopplerDest *)RVAL2BOXED(obj, POPPLER_TYPE_DEST))
13
+ #define DEST2RVAL(obj) (BOXED2RVAL(obj, POPPLER_TYPE_DEST))
14
+
15
+ #define DEST_TYPE2RVAL(obj) (GENUM2RVAL(obj, POPPLER_TYPE_DEST_TYPE))
16
+ #define ACTION_TYPE2RVAL(obj) (GENUM2RVAL(obj, POPPLER_TYPE_ACTION_TYPE))
17
+ #define RVAL2ACTION_TYPE(obj) (RVAL2GENUM(obj, POPPLER_TYPE_ACTION_TYPE))
18
+
19
+
20
+ /* PopplerAction */
21
+ static VALUE actions[POPPLER_ACTION_MOVIE + 1];
22
+
23
+ VALUE
24
+ rb_poppler_ruby_object_from_action(PopplerAction *action)
25
+ {
26
+ VALUE obj;
27
+
28
+ if (!action)
29
+ return Qnil;
30
+
31
+ obj = BOXED2RVAL(action, POPPLER_TYPE_ACTION);
32
+ RBASIC(obj)->klass = actions[action->type];
33
+ return obj;
34
+ }
35
+
36
+ PopplerAction *
37
+ rb_poppler_action_from_ruby_object(VALUE action)
38
+ {
39
+ return NIL_P(action) ? NULL : RVAL2BOXED(action, POPPLER_TYPE_ACTION);
40
+ }
41
+
42
+ #define ACTION_ATTR_STR(type, name) \
43
+ static VALUE \
44
+ action_ ## type ## _ ## name (VALUE self) \
45
+ { \
46
+ return CSTR2RVAL(RVAL2POPPLER_ACTION(self)->type.name); \
47
+ }
48
+
49
+ #define ACTION_ATTR_DEST(type, name) \
50
+ static VALUE \
51
+ action_ ## type ## _ ## name (VALUE self) \
52
+ { \
53
+ return DEST2RVAL(RVAL2POPPLER_ACTION(self)->type.name); \
54
+ }
55
+
56
+ #define DEFINE_ACCESSOR(prefix, target, name) \
57
+ rb_define_method(target, G_STRINGIFY(name), prefix ## _ ## name, 0);
58
+
59
+ #define DEFINE_ACTION_ACCESSOR(target, type, name) \
60
+ DEFINE_ACCESSOR(action_ ## type, target, name)
61
+
62
+ /* PopplerActionAny */
63
+ static VALUE
64
+ action_any_type(VALUE self)
65
+ {
66
+ return ACTION_TYPE2RVAL(RVAL2POPPLER_ACTION(self)->type);
67
+ }
68
+ ACTION_ATTR_STR(any, title);
69
+
70
+ /* PopplerActionGotoDest */
71
+ ACTION_ATTR_DEST(goto_dest, dest);
72
+
73
+ /* PopplerActionGotoRemote */
74
+ ACTION_ATTR_STR(goto_remote, file_name);
75
+ ACTION_ATTR_DEST(goto_remote, dest);
76
+
77
+ /* PopplerActionLaunch */
78
+ ACTION_ATTR_STR(launch, file_name);
79
+ ACTION_ATTR_STR(launch, params);
80
+
81
+ /* PopplerActionUri */
82
+ ACTION_ATTR_STR(uri, uri);
83
+
84
+ /* PopplerActionNamed */
85
+ ACTION_ATTR_STR(named, named_dest);
86
+
87
+ /* PopplerActionMovie */
88
+
89
+
90
+ /* PopplerDest */
91
+ #ifdef RB_POPPLER_TYPE_DEST_NOT_DEFINED
92
+ GType
93
+ poppler_dest_get_type (void)
94
+ {
95
+ static GType our_type = 0;
96
+
97
+ if (our_type == 0)
98
+ our_type = g_boxed_type_register_static ("PopplerDest",
99
+ (GBoxedCopyFunc) poppler_dest_copy,
100
+ (GBoxedFreeFunc) poppler_dest_free);
101
+
102
+ return our_type;
103
+ }
104
+ #endif
105
+
106
+ static VALUE
107
+ dest_get_type(VALUE self)
108
+ {
109
+ return DEST_TYPE2RVAL(RVAL2DEST(self)->type);
110
+ }
111
+
112
+ #define DEST_ATTR_INT(name) \
113
+ static VALUE \
114
+ dest_ ## name (VALUE self) \
115
+ { \
116
+ return INT2NUM(RVAL2DEST(self)->name); \
117
+ }
118
+
119
+ #define DEST_ATTR_UINT(name) \
120
+ static VALUE \
121
+ dest_ ## name (VALUE self) \
122
+ { \
123
+ return UINT2NUM(RVAL2DEST(self)->name); \
124
+ }
125
+
126
+ #define DEST_ATTR_DOUBLE(name) \
127
+ static VALUE \
128
+ dest_ ## name (VALUE self) \
129
+ { \
130
+ return rb_float_new(RVAL2DEST(self)->name); \
131
+ }
132
+
133
+ #define DEST_ATTR_STR(name) \
134
+ static VALUE \
135
+ dest_ ## name (VALUE self) \
136
+ { \
137
+ return CSTR2RVAL(RVAL2DEST(self)->name); \
138
+ }
139
+
140
+ #define DEFINE_DEST_ACCESSOR(target, name) \
141
+ DEFINE_ACCESSOR(dest, target, name)
142
+
143
+
144
+ DEST_ATTR_INT(page_num)
145
+ DEST_ATTR_DOUBLE(left)
146
+ DEST_ATTR_DOUBLE(bottom)
147
+ DEST_ATTR_DOUBLE(right)
148
+ DEST_ATTR_DOUBLE(top)
149
+ DEST_ATTR_DOUBLE(zoom)
150
+ DEST_ATTR_STR(named_dest)
151
+ DEST_ATTR_UINT(change_left)
152
+ DEST_ATTR_UINT(change_top)
153
+ DEST_ATTR_UINT(change_zoom)
154
+
155
+ void
156
+ Init_poppler_action(VALUE mPoppler)
157
+ {
158
+ VALUE cDest, cAction, cActionAny, cActionGotoDest, cActionGotoRemote;
159
+ VALUE cActionLaunch, cActionUri, cActionNamed, cActionMovie;
160
+
161
+ cAction = G_DEF_CLASS(POPPLER_TYPE_ACTION, "Action", mPoppler);
162
+
163
+ cActionAny = rb_define_class_under(mPoppler, "ActionAny", cAction);
164
+ rb_define_method(cActionAny, "type", action_any_type, 0);
165
+ DEFINE_ACTION_ACCESSOR(cActionAny, any, title);
166
+
167
+ cActionGotoDest = rb_define_class_under(mPoppler, "ActionGotoDest",
168
+ cActionAny);
169
+ DEFINE_ACTION_ACCESSOR(cActionGotoDest, goto_dest, dest);
170
+
171
+ cActionGotoRemote = rb_define_class_under(mPoppler, "ActionGotoRemote",
172
+ cActionAny);
173
+ DEFINE_ACTION_ACCESSOR(cActionGotoRemote, goto_remote, file_name);
174
+ DEFINE_ACTION_ACCESSOR(cActionGotoRemote, goto_remote, dest);
175
+
176
+ cActionLaunch = rb_define_class_under(mPoppler, "ActionLaunch", cActionAny);
177
+ DEFINE_ACTION_ACCESSOR(cActionLaunch, launch, file_name);
178
+ DEFINE_ACTION_ACCESSOR(cActionLaunch, launch, params);
179
+
180
+ cActionUri = rb_define_class_under(mPoppler, "ActionUri", cActionAny);
181
+ DEFINE_ACTION_ACCESSOR(cActionUri, uri, uri);
182
+
183
+ cActionNamed = rb_define_class_under(mPoppler, "ActionNamed", cActionAny);
184
+ DEFINE_ACTION_ACCESSOR(cActionNamed, named, named_dest);
185
+
186
+ cActionMovie = rb_define_class_under(mPoppler, "ActionMovie", cActionAny);
187
+
188
+ actions[POPPLER_ACTION_UNKNOWN] = cActionAny;
189
+ actions[POPPLER_ACTION_GOTO_DEST] = cActionGotoDest;
190
+ actions[POPPLER_ACTION_GOTO_REMOTE] = cActionGotoRemote;
191
+ actions[POPPLER_ACTION_LAUNCH] = cActionLaunch;
192
+ actions[POPPLER_ACTION_URI] = cActionUri;
193
+ actions[POPPLER_ACTION_NAMED] = cActionNamed;
194
+ actions[POPPLER_ACTION_MOVIE] = cActionMovie;
195
+
196
+ G_DEF_SETTERS(cAction);
197
+ G_DEF_SETTERS(cActionAny);
198
+ G_DEF_SETTERS(cActionGotoDest);
199
+ G_DEF_SETTERS(cActionGotoRemote);
200
+ G_DEF_SETTERS(cActionLaunch);
201
+ G_DEF_SETTERS(cActionUri);
202
+ G_DEF_SETTERS(cActionNamed);
203
+ G_DEF_SETTERS(cActionMovie);
204
+
205
+ G_DEF_CLASS(POPPLER_TYPE_ACTION_TYPE, "ActionType", mPoppler);
206
+ G_DEF_CLASS(POPPLER_TYPE_DEST_TYPE, "DestType", mPoppler);
207
+
208
+ cDest = G_DEF_CLASS(POPPLER_TYPE_DEST, "Dest", mPoppler);
209
+
210
+ rb_define_method(cDest, "type", dest_get_type, 0);
211
+ DEFINE_DEST_ACCESSOR(cDest, page_num);
212
+ DEFINE_DEST_ACCESSOR(cDest, left);
213
+ DEFINE_DEST_ACCESSOR(cDest, bottom);
214
+ DEFINE_DEST_ACCESSOR(cDest, right);
215
+ DEFINE_DEST_ACCESSOR(cDest, top);
216
+ DEFINE_DEST_ACCESSOR(cDest, zoom);
217
+ DEFINE_DEST_ACCESSOR(cDest, named_dest);
218
+ DEFINE_DEST_ACCESSOR(cDest, change_left);
219
+ DEFINE_DEST_ACCESSOR(cDest, change_top);
220
+ DEFINE_DEST_ACCESSOR(cDest, change_zoom);
221
+ G_DEF_SETTERS(cDest);
222
+ }