class HexaPDF::Type::Image
Represents an image XObject of a PDF document.
See: PDF1.7 s8.8
Constants
- Info
The structure that is returned by the
Image#info
method.
Attributes
Returns the source path that was used when creating the image object.
This value is only set when the image object was created by using the image loading facility and not when the image is part of a loaded PDF file.
Public Instance Methods
Returns the height of the image.
# File lib/hexapdf/type/image.rb, line 92 def height self[:Height] end
Returns an Info
structure with information about the image.
Available accessors:
- type
-
The type of the image. Either :jpeg, :jp2, :jbig2, :ccitt or :png.
- width
-
The width of the image.
- height
-
The height of the image.
- color_space
-
The color space the image uses. Either :rgb, :cmyk, :gray or :other.
- indexed
-
Whether the image uses an indexed color space or not.
- components
-
The number of color components of the color space, or -1 if the number couldn't be determined.
- bits_per_component
-
The number of bits per color component.
- writable
-
Whether the image can be written by
HexaPDF
. - extension
-
The file extension that would be used when writing the file. Either jpg, jpx or png. Only meaningful when writable is true.
# File lib/hexapdf/type/image.rb, line 120 def info result = Info.new result.width = self[:Width] result.height = self[:Height] result.bits_per_component = self[:BitsPerComponent] result.indexed = false result.writable = true filter, rest = *self[:Filter] case filter when :DCTDecode result.type = :jpeg result.extension = 'jpg' when :JPXDecode result.type = :jp2 result.extension = 'jpx' when :JBIG2Decode result.type = :jbig2 when :CCITTFaxDecode result.type = :ccitt else result.type = :png result.extension = 'png' end if rest || ![:FlateDecode, :DCTDecode, :JPXDecode, nil].include?(filter) result.writable = false end color_space, = *self[:ColorSpace] if color_space == :Indexed result.indexed = true color_space, = *self[:ColorSpace][1] end case color_space when :DeviceRGB, :CalRGB result.color_space = :rgb result.components = 3 when :DeviceGray, :CalGray result.color_space = :gray result.components = 1 when :DeviceCMYK result.color_space = :cmyk result.components = 4 result.writable = false if result.type == :png else result.color_space = :other result.components = -1 result.writable = false if result.type == :png end result.writable = false if self[:SMask] result end
Returns the width of the image.
# File lib/hexapdf/type/image.rb, line 87 def width self[:Width] end
Saves this image XObject to the file with the given name and appends the correct extension (if the name already contains this extension, the name is used as is), or the given IO object.
Raises an error if the image format is not supported.
The output format and extension depends on the image type as returned by the info
method:
- :jpeg
-
Saved as a JPEG file with the extension '.jpg'
- :jp2
-
Saved as a JPEG2000 file with the extension '.jpx'
- :png
-
Saved as a PNG file with the extension '.png'
# File lib/hexapdf/type/image.rb, line 191 def write(name_or_io) info = self.info unless info.writable raise HexaPDF::Error, "PDF image format not supported for writing" end io = if name_or_io.kind_of?(String) File.open(name_or_io.sub(/\.#{info.extension}\z/, '') << "." << info.extension, "wb") else name_or_io end if info.type == :jpeg || info.type == :jp2 source = stream_source while source.alive? && (chunk = source.resume) io << chunk end else write_png(io, info) end ensure io.close if io && name_or_io.kind_of?(String) end
Private Instance Methods
Returns the binary representation of the PNG chunk for the given chunk type and data.
# File lib/hexapdf/type/image.rb, line 275 def png_chunk(type, data = '') [data.length].pack("N") << type << data << [Zlib.crc32(data, Zlib.crc32(type))].pack("N") end
Writes the image as PNG to the given IO stream.
# File lib/hexapdf/type/image.rb, line 219 def write_png(io, info) io << ImageLoader::PNG::MAGIC_FILE_MARKER color_type = if info.indexed ImageLoader::PNG::INDEXED elsif info.color_space == :rgb ImageLoader::PNG::TRUECOLOR else ImageLoader::PNG::GREYSCALE end io << png_chunk('IHDR', [info.width, info.height, info.bits_per_component, color_type, 0, 0, 0].pack('N2C5')) if key?(:Intent) # PNG s11.3.3.5 intent = ImageLoader::PNG::RENDERING_INTENT_MAP.rassoc(self[:Intent]).first io << png_chunk('sRGB', intent.chr) << png_chunk('gAMA', [45455].pack('N')) << png_chunk('cHRM', [31270, 32900, 64000, 33000, 30000, 60000, 15000, 6000].pack('N8')) end if color_type == ImageLoader::PNG::INDEXED palette_data = self[:ColorSpace][3] palette_data = palette_data.stream unless palette_data.kind_of?(String) palette = ''.b if info.color_space == :rgb palette = palette_data[0, palette_data.length - palette_data.length % 3] else palette_data.each_byte {|byte| palette << byte << byte << byte } end io << png_chunk('PLTE', palette) end if self[:Mask].kind_of?(PDFArray) && self[:Mask].each_slice(2).all? {|a, b| a == b } && (color_type == ImageLoader::PNG::TRUECOLOR || color_type == ImageLoader::PNG::GREYSCALE) io << png_chunk('tRNS', self[:Mask].each_slice(2).map {|a, _| a }.pack('n*')) end filter, = *self[:Filter] decode_parms, = *self[:DecodeParms] if filter == :FlateDecode && decode_parms && decode_parms[:Predictor].to_i >= 10 data = stream_source else colors = (color_type == ImageLoader::PNG::INDEXED ? 1 : info.components) flate_decode = config.constantize('filter.map', :FlateDecode) data = flate_decode.encoder(stream_decoder, Predictor: 15, Colors: colors, Columns: info.width, BitsPerComponent: info.bits_per_component) end io << png_chunk('IDAT', Filter.string_from_source(data)) io << png_chunk('IEND') end