Class: Image

Inherits:
Document show all
Includes:
Zena::Use::Upload::UploadedFile
Defined in:
app/models/image.rb

Overview

An Image is a Document with a file that we can view inline. An image can be displayed in various formats (defined through modes). These modes are defined for each Site through Iformat. Default modes:

'tiny' => { :size=>:force, :width=>16,  :height=>16,                },
'mini' => { :size=>:force, :width=>32,  :ratio=>1.0,                },
'pv'   => { :size=>:force, :width=>70,  :ratio=>1.0                 },
'med'  => { :size=>:limit, :width=>280, :ratio=>2/3.0               },
'top'  => { :size=>:force, :width=>280, :ratio=>2.0/3.0, :gravity=>Magick::NorthGravity},
'mid'  => { :size=>:force, :width=>280, :ratio=>2.0/3.0, :gravity=>Magick::CenterGravity},
'low'  => { :size=>:force, :width=>280, :ratio=>2.0/3.0, :gravity=>Magick::SouthGravity},
'edit' => { :size=>:limit, :width=>400, :height=>400                },
'std'  => { :size=>:limit, :width=>600, :ratio=>2/3.0               },
'full' => { :size=>:keep },

To display an image with one of those formats, you use the ‘img_tag’ helper :

img_tag(@node, :mode=>'med')

For more information on img_tag, have a look at ApplicationHelper#img_tag.

An image can be croped by changing the ‘crop’ pseudo attribute (see Image#crop= ) :

@node.update_attributes(:crop=>{:x=>10, :y=>10, :width=>30, :height=>60})

Version

The version class used by images is the ImageVersion.

Storage

File data is managed by the Document Attachment. This class is responsible for storing the file and retrieving the data.

Properties

These properties are added to Images :

size(format)::    file size for the image at the given format
width(format)::   image width in pixel for the given format
height(format)::  image height in pixel for the given format

Default links for Image are:

icon_for

become the unique ‘icon’ for the linked node.

Example on how to use ‘icon’ with ruby:

@node.icon.img_tag('pv')   <= display the node's icon with the 'pv' (preview) format.

Same example in a zafu template:

<r:img src='icon' format='pv'/>

or to create a link to the article using the icon:

<r:img src='icon' format='pv' href='self'/>

Constant Summary

Constants inherited from Node

Node::Caster, Node::VERSION_ATTRIBUTES

Constants included from Zena::Use::Workflow

Zena::Use::Workflow::WORKFLOW_ATTRIBUTES

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Document

accept_content_type_change?, document_class_from_content_type, #filename, #filepath, #image?, new, o_new, #size, version_class

Methods inherited from Node

#all_relations, allowed_change_to_classes, #archive, #asset_path, #author, author_proc, auto_create_discussion, #can_auto_create_discussion?, #can_comment?, cast_to_class, class_for_relation, classes_for_form, #comments, #comments_count, #content_lang, create_node, create_nodes_from_folder, create_or_update_node, #data, #discussion, #dyn_attribute_keys, #empty?, #export_keys, #export_to_folder, extract_archive, find_by_parent_title_and_kpath, find_by_title, find_by_zip, find_node_by_pseudo, #find_node_by_pseudo, get_attributes_from_yaml, get_class, #get_discussion_id, #get_project_id, get_role, #get_section_id, inherited, inspect, #klass, #klass=, #klass_changed?, kpath_match?, #kpath_match?, kpaths_for_form, load_unhandled_children, #login_info, #m_author, #m_author=, #m_text, #m_text=, #m_title, #m_title=, make_schema, #merge_multi_errors, native_classes, native_classes_by_name, new, #new_child, new_node, #o_skin, #o_user, #parent, #parent_zip, #parent_zip=, #parse_assets, #parse_keys, plural_relation?, #project, #project_zip, #rcast, #real_project, #real_section, #reload, #replace_attributes_in_values, #safe_method_type, #safe_send, #section, #section_zip, #skin, #skin_zip, #skin_zip=, #sweep_cache, #to_yaml, transform_attributes, translate_pseudo_id, #unparse_assets, #update_attributes_with_transformation, #user, #user_zip, #v_number, #vclass, #versions_with_secure, #virtual_class, #virtual_class=, #vkind_of?, zafu_attribute, #zafu_eval, #zafu_versions

Methods included from Zena::Use::Search::NodeClassMethods

#match_query, #search_index, #search_records, #search_text

Methods included from Zena::Acts::Secure

#secure_scope, #secure_write_scope, #visitor=

Methods included from Zena::Acts::Secure::SecureResult

#construct_id_map, #secure_result

Methods included from Zena::Acts::SecureNode

#acts_as_secure_node

Methods included from Zena::Use::Dates::ModelMethods

included

Methods included from Zena::Use::Dates::Common

#format_date

Methods included from Zena::Use::QueryNode::ModelMethods

#db_attr, #find, included, #safe_first, #start_node_zip

Methods included from Zena::Use::ScopeIndex::ModelMethods

included, #rebuild_index_with_scope_index!, #rebuild_scope_index!, #scope_index, scope_index_proc

Methods included from Zena::Use::Relations::ModelMethods

#add_link, #all_relations, included, #l_comment, #l_comment=, #l_date, #l_date=, #l_status, #l_status=, #link_id, #link_id=, #linked_node, #linked_node=, #rel, #rel=, #rel_attributes=, #relation_alias, #relation_links, #relation_proxy, #relation_proxy_from_link, #relations_for_form, #remove_link

Methods included from Zena::Use::Fulltext::ModelMethods

included, #rebuild_index_for_version_with_fulltext

Methods included from Zena::Use::PropEval::ModelMethods

included, #merge_prop_eval, #need_set__id, #rebuild_index_for_version_with_prop_eval, #set__id

Methods included from Zena::Use::VersionHash::ModelMethods

#rebuild_vhash, #v_public?, #version, #version_id, #vhash, #visible_versions

Methods included from Zena::Acts::Serializable::ModelMethods

#all_link_ids, #default_serialization_options, #export_ids, #export_properties, included, #to_xml

Methods included from Zena::Use::Ancestry::ModelMethods

#ancestors, #basepath, #fullpath_as_title, included, #is_ancestor?, #pseudo_id, #short_path, #z_ancestors

Methods included from Zena::Use::Workflow

#after_all, #apply, #apply_with_callbacks, #auto_publish?, #can_apply?, #can_destroy_version?, #can_edit?, #can_propose?, #can_publish?, #can_refuse?, #can_remove?, #can_unpublish?, #can_update?, #destroy_version, #edit_content!, #get_publish_from, included, #propose, #publish, #redit, #refuse, #remove, #set_current_transition, #traductions, #transition_allowed?, #transition_for, #unpublish, #would_change_original?

Methods included from Zena::Use::NestedAttributesAlias::ModelMethods

#attributes_with_nested_alias=, included, #nested_model_names_for_alias, #resolve_attributes_alias

Methods included from Zena::Acts::Enrollable::ModelMethods

#assigned_roles, #has_role?, included, #zafu_possible_roles

Methods included from Zena::Use::FieldIndex::ModelMethods

included, #property_field_index

Methods included from Zena::Use::MLIndex::ModelMethods

included, #index_reader, #rebuild_index_for_version, #rebuild_index_with_multi_lingual!

Methods included from Zena::Use::Kpath::InstanceMethods

included

Dynamic Method Handling

This class handles dynamic methods through the method_missing method in the class Zena::Use::Relations::ModelMethods

Class Method Details

.accept_content_type?(content_type) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
# File 'app/models/image.rb', line 73

def accept_content_type?(content_type)
  Zena::Use::ImageBuilder.image_content_type?(content_type)
end

.change_to_classes_for_formObject

Class list to which this class can change to



78
79
80
# File 'app/models/image.rb', line 78

def change_to_classes_for_form
  classes_for_form(:class => 'Image')
end

Instance Method Details

#can_crop?(format) ⇒ Boolean

Returns:

  • (Boolean)


165
166
167
168
169
170
# File 'app/models/image.rb', line 165

def can_crop?(format)
  x, y, w, h = [format['x'].to_i, 0].max, [format['y'].to_i, 0].max, [format['w'].to_i, width].min, [format['h'].to_i, height].min
  (format['max_value'] && (format['max_value'].to_f * (format['max_unit'] == 'Mb' ? 1024 : 1) * 1024) < prop['size']) ||
  (format['format'] && format['format'] != prop['ext']) ||
  ((x < width && y < height && w > 0 && h > 0) && !(x==0 && y==0 && w == width && h == height))
end

#crop=(format) ⇒ Object

Crop the image using the ‘crop’ hash with the top left corner position (:x, :y) and the width and height (:width, :heigt). Example:

@node.crop = {:x=>10, :y=>10, :width=>30, :height=>60}

Be carefull as this method changes the current file. So you should make a backup version before croping the image (the popup editor displays a warning).



175
176
177
178
179
180
181
182
183
# File 'app/models/image.rb', line 175

def crop=(format)
  if can_crop?(format)
    # do crop
    if file = self.cropped_file(format)
      # crop can return nil, check first.
      self.file = file
    end
  end
end

#cropped_file(format) ⇒ Object

Return a cropped image using the ‘crop’ hash with the top left corner position (:x, :y) and the width and height (:width, :heigt).



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'app/models/image.rb', line 187

def cropped_file(format)
  original   = format['original'] || @loaded_file || self.file
  x, y, w, h = format['x'].to_f, format['y'].to_f, format['w'].to_f, format['h'].to_f
  new_type   = format['format'] ? Zena::EXT_TO_TYPE[format['format'].downcase][0] : nil
  max        = format['max_value'].to_f * (format['max_unit'] == 'Mb' ? 1024 : 1) * 1024

  # crop image
  img = Zena::Use::ImageBuilder.new(:file => original)
  img.crop!(x, y, w, h) if x && y && w && h
  img.format       = format['format'] if new_type && new_type != content_type
  img.max_filesize = max if format['max_value'] && max

  file = Tempfile.new(filename)
  File.open(file.path, "wb") { |f| f.syswrite(img.read) }

  ctype = Zena::EXT_TO_TYPE[img.format.downcase][0]
  fname = "#{filename}.#{Zena::TYPE_TO_EXT[ctype][0]}"
  uploaded_file(file, filename, ctype)
end

#exifObject

Return the Exchangeable Image Format (Exif).



114
115
116
# File 'app/models/image.rb', line 114

def exif
  prop['exif'] || ExifData.new({})
end

#file(format = nil) ⇒ Object

Return a file with the data for the given format. It is the receiver’s responsability to close the file.



153
154
155
156
157
158
159
160
161
162
163
# File 'app/models/image.rb', line 153

def file(format=nil)
  if format.nil? || format[:size] == :keep
    super()
  else
    if File.exist?(self.filepath(format)) || make_image(format)
      File.new(self.filepath(format))
    else
      nil
    end
  end
end

#file=(file) ⇒ Object

Set content file, will refuse to accept the file if it is not an image.



140
141
142
143
144
145
146
147
148
149
150
# File 'app/models/image.rb', line 140

def file=(file)
  new_file = super
  if self.class.accept_content_type?(new_file.content_type)
    @new_image = new_file
    img = image_with_format(nil)

    prop['width' ] = img.width
    prop['height'] = img.height
    prop['exif']   = img.exif rescue nil
  end
end

#filesize(format = nil) ⇒ Object

Return the size of the image for the given format (see Image for information on format).



119
120
121
# File 'app/models/image.rb', line 119

def filesize(format=nil)
  version.filesize(format)
end

#fix_sizes(w, h) ⇒ Object

This is called if the image’s width and/or height is nil and image builder could compute the size.



209
210
211
212
213
# File 'app/models/image.rb', line 209

def fix_sizes(w, h)
  prop['width']  = w
  prop['height'] = h
  Zena::Db.set_attribute(version, 'properties', encode_properties(@properties))
end

#height(format = nil) ⇒ Object

Return the height in pixels for an image at the given format.



99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'app/models/image.rb', line 99

def height(format=nil)
  if format.nil? || format[:size] == :keep
    prop['height']
  elsif format[:size] == :force
    format[:height]
  else
    if img = image_with_format(format)
      img.height
    else
      nil
    end
  end
end

#update_attributes(attributes) ⇒ Object

Updaging image attributes and propreties. Accept also :file and :crop keys.



124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'app/models/image.rb', line 124

def update_attributes(attributes)
  attributes.stringify_keys!
  # If file and crop attributes are both present when updating, make sure to run file= before crop=.
  if attributes['file'] && attributes['crop']
    file = attributes.delete('file')
    crop = attributes.delete('crop')
    super(attributes)
    self.file = file
    self.crop = crop
    save
  else
    super
  end
end

#width(format = nil) ⇒ Object

Return the width in pixels for an image at the given format.



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'app/models/image.rb', line 84

def width(format=nil)
  if format.nil? || format[:size] == :keep
    prop['width']
  elsif format[:size] == :force
    format[:width]
  else
    if img = image_with_format(format)
      img.width
    else
      nil
    end
  end
end