JPEG (Joint Photographic Experts Group) File Interchange Format: Go parsing library

JPEG File Interchange Format, or JFIF, or, more colloquially known as just "JPEG" or "JPG", is a popular 2D bitmap image file format, offering lossy compression which works reasonably well with photographic images.

Format is organized as a container format, serving multiple "segments", each starting with a magic and a marker. JFIF standard dictates order and mandatory apperance of segments:

  • SOI
  • APP0 (with JFIF magic)
  • APP0 (with JFXX magic, optional)
  • everything else
  • SOS
  • JPEG-compressed stream
  • EOI

File extension

["jpg", "jpeg", "jpe", "jif", "jfif", "jfi"]

KS implementation details

License: CC0-1.0

References

This page hosts a formal specification of JPEG (Joint Photographic Experts Group) File Interchange Format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.

Go source code to parse JPEG (Joint Photographic Experts Group) File Interchange Format

jpeg.go

// Code generated by kaitai-struct-compiler from a .ksy source file. DO NOT EDIT.

import (
	"github.com/kaitai-io/kaitai_struct_go_runtime/kaitai"
	"bytes"
)


/**
 * JPEG File Interchange Format, or JFIF, or, more colloquially known
 * as just "JPEG" or "JPG", is a popular 2D bitmap image file format,
 * offering lossy compression which works reasonably well with
 * photographic images.
 * 
 * Format is organized as a container format, serving multiple
 * "segments", each starting with a magic and a marker. JFIF standard
 * dictates order and mandatory apperance of segments:
 * 
 * * SOI
 * * APP0 (with JFIF magic)
 * * APP0 (with JFXX magic, optional)
 * * everything else
 * * SOS
 * * JPEG-compressed stream
 * * EOI
 */

type Jpeg_ComponentId int
const (
	Jpeg_ComponentId__Y Jpeg_ComponentId = 1
	Jpeg_ComponentId__Cb Jpeg_ComponentId = 2
	Jpeg_ComponentId__Cr Jpeg_ComponentId = 3
	Jpeg_ComponentId__I Jpeg_ComponentId = 4
	Jpeg_ComponentId__Q Jpeg_ComponentId = 5
)
var values_Jpeg_ComponentId = map[Jpeg_ComponentId]struct{}{1: {}, 2: {}, 3: {}, 4: {}, 5: {}}
func (v Jpeg_ComponentId) isDefined() bool {
	_, ok := values_Jpeg_ComponentId[v]
	return ok
}
type Jpeg struct {
	Segments []*Jpeg_Segment
	_io *kaitai.Stream
	_root *Jpeg
	_parent kaitai.Struct
}
func NewJpeg() *Jpeg {
	return &Jpeg{
	}
}

func (this Jpeg) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg) Read(io *kaitai.Stream, parent kaitai.Struct, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	for i := 0;; i++ {
		tmp1, err := this._io.EOF()
		if err != nil {
			return err
		}
		if tmp1 {
			break
		}
		tmp2 := NewJpeg_Segment()
		err = tmp2.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Segments = append(this.Segments, tmp2)
	}
	return err
}
type Jpeg_ExifInJpeg struct {
	ExtraZero []byte
	Data *Exif
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg_SegmentApp1
	_raw_Data []byte
}
func NewJpeg_ExifInJpeg() *Jpeg_ExifInJpeg {
	return &Jpeg_ExifInJpeg{
	}
}

func (this Jpeg_ExifInJpeg) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_ExifInJpeg) Read(io *kaitai.Stream, parent *Jpeg_SegmentApp1, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp3, err := this._io.ReadBytes(int(1))
	if err != nil {
		return err
	}
	tmp3 = tmp3
	this.ExtraZero = tmp3
	if !(bytes.Equal(this.ExtraZero, []uint8{0})) {
		return kaitai.NewValidationNotEqualError([]uint8{0}, this.ExtraZero, this._io, "/types/exif_in_jpeg/seq/0")
	}
	tmp4, err := this._io.ReadBytesFull()
	if err != nil {
		return err
	}
	tmp4 = tmp4
	this._raw_Data = tmp4
	_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data))
	tmp5 := NewExif()
	err = tmp5.Read(_io__raw_Data, nil, nil)
	if err != nil {
		return err
	}
	this.Data = tmp5
	return err
}

type Jpeg_Segment_MarkerEnum int
const (
	Jpeg_Segment_MarkerEnum__Tem Jpeg_Segment_MarkerEnum = 1
	Jpeg_Segment_MarkerEnum__Sof0 Jpeg_Segment_MarkerEnum = 192
	Jpeg_Segment_MarkerEnum__Sof1 Jpeg_Segment_MarkerEnum = 193
	Jpeg_Segment_MarkerEnum__Sof2 Jpeg_Segment_MarkerEnum = 194
	Jpeg_Segment_MarkerEnum__Sof3 Jpeg_Segment_MarkerEnum = 195
	Jpeg_Segment_MarkerEnum__Dht Jpeg_Segment_MarkerEnum = 196
	Jpeg_Segment_MarkerEnum__Sof5 Jpeg_Segment_MarkerEnum = 197
	Jpeg_Segment_MarkerEnum__Sof6 Jpeg_Segment_MarkerEnum = 198
	Jpeg_Segment_MarkerEnum__Sof7 Jpeg_Segment_MarkerEnum = 199
	Jpeg_Segment_MarkerEnum__Soi Jpeg_Segment_MarkerEnum = 216
	Jpeg_Segment_MarkerEnum__Eoi Jpeg_Segment_MarkerEnum = 217
	Jpeg_Segment_MarkerEnum__Sos Jpeg_Segment_MarkerEnum = 218
	Jpeg_Segment_MarkerEnum__Dqt Jpeg_Segment_MarkerEnum = 219
	Jpeg_Segment_MarkerEnum__Dnl Jpeg_Segment_MarkerEnum = 220
	Jpeg_Segment_MarkerEnum__Dri Jpeg_Segment_MarkerEnum = 221
	Jpeg_Segment_MarkerEnum__Dhp Jpeg_Segment_MarkerEnum = 222
	Jpeg_Segment_MarkerEnum__App0 Jpeg_Segment_MarkerEnum = 224
	Jpeg_Segment_MarkerEnum__App1 Jpeg_Segment_MarkerEnum = 225
	Jpeg_Segment_MarkerEnum__App2 Jpeg_Segment_MarkerEnum = 226
	Jpeg_Segment_MarkerEnum__App3 Jpeg_Segment_MarkerEnum = 227
	Jpeg_Segment_MarkerEnum__App4 Jpeg_Segment_MarkerEnum = 228
	Jpeg_Segment_MarkerEnum__App5 Jpeg_Segment_MarkerEnum = 229
	Jpeg_Segment_MarkerEnum__App6 Jpeg_Segment_MarkerEnum = 230
	Jpeg_Segment_MarkerEnum__App7 Jpeg_Segment_MarkerEnum = 231
	Jpeg_Segment_MarkerEnum__App8 Jpeg_Segment_MarkerEnum = 232
	Jpeg_Segment_MarkerEnum__App9 Jpeg_Segment_MarkerEnum = 233
	Jpeg_Segment_MarkerEnum__App10 Jpeg_Segment_MarkerEnum = 234
	Jpeg_Segment_MarkerEnum__App11 Jpeg_Segment_MarkerEnum = 235
	Jpeg_Segment_MarkerEnum__App12 Jpeg_Segment_MarkerEnum = 236
	Jpeg_Segment_MarkerEnum__App13 Jpeg_Segment_MarkerEnum = 237
	Jpeg_Segment_MarkerEnum__App14 Jpeg_Segment_MarkerEnum = 238
	Jpeg_Segment_MarkerEnum__App15 Jpeg_Segment_MarkerEnum = 239
	Jpeg_Segment_MarkerEnum__Com Jpeg_Segment_MarkerEnum = 254
)
var values_Jpeg_Segment_MarkerEnum = map[Jpeg_Segment_MarkerEnum]struct{}{1: {}, 192: {}, 193: {}, 194: {}, 195: {}, 196: {}, 197: {}, 198: {}, 199: {}, 216: {}, 217: {}, 218: {}, 219: {}, 220: {}, 221: {}, 222: {}, 224: {}, 225: {}, 226: {}, 227: {}, 228: {}, 229: {}, 230: {}, 231: {}, 232: {}, 233: {}, 234: {}, 235: {}, 236: {}, 237: {}, 238: {}, 239: {}, 254: {}}
func (v Jpeg_Segment_MarkerEnum) isDefined() bool {
	_, ok := values_Jpeg_Segment_MarkerEnum[v]
	return ok
}
type Jpeg_Segment struct {
	Magic []byte
	Marker Jpeg_Segment_MarkerEnum
	Length uint16
	Data interface{}
	ImageData []byte
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg
	_raw_Data []byte
}
func NewJpeg_Segment() *Jpeg_Segment {
	return &Jpeg_Segment{
	}
}

func (this Jpeg_Segment) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_Segment) Read(io *kaitai.Stream, parent *Jpeg, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp6, err := this._io.ReadBytes(int(1))
	if err != nil {
		return err
	}
	tmp6 = tmp6
	this.Magic = tmp6
	if !(bytes.Equal(this.Magic, []uint8{255})) {
		return kaitai.NewValidationNotEqualError([]uint8{255}, this.Magic, this._io, "/types/segment/seq/0")
	}
	tmp7, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Marker = Jpeg_Segment_MarkerEnum(tmp7)
	if ( ((this.Marker != Jpeg_Segment_MarkerEnum__Soi) && (this.Marker != Jpeg_Segment_MarkerEnum__Eoi)) ) {
		tmp8, err := this._io.ReadU2be()
		if err != nil {
			return err
		}
		this.Length = uint16(tmp8)
	}
	if ( ((this.Marker != Jpeg_Segment_MarkerEnum__Soi) && (this.Marker != Jpeg_Segment_MarkerEnum__Eoi)) ) {
		switch (this.Marker) {
		case Jpeg_Segment_MarkerEnum__App0:
			tmp9, err := this._io.ReadBytes(int(this.Length - 2))
			if err != nil {
				return err
			}
			tmp9 = tmp9
			this._raw_Data = tmp9
			_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data))
			tmp10 := NewJpeg_SegmentApp0()
			err = tmp10.Read(_io__raw_Data, this, this._root)
			if err != nil {
				return err
			}
			this.Data = tmp10
		case Jpeg_Segment_MarkerEnum__App1:
			tmp11, err := this._io.ReadBytes(int(this.Length - 2))
			if err != nil {
				return err
			}
			tmp11 = tmp11
			this._raw_Data = tmp11
			_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data))
			tmp12 := NewJpeg_SegmentApp1()
			err = tmp12.Read(_io__raw_Data, this, this._root)
			if err != nil {
				return err
			}
			this.Data = tmp12
		case Jpeg_Segment_MarkerEnum__Sof0:
			tmp13, err := this._io.ReadBytes(int(this.Length - 2))
			if err != nil {
				return err
			}
			tmp13 = tmp13
			this._raw_Data = tmp13
			_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data))
			tmp14 := NewJpeg_SegmentSof0()
			err = tmp14.Read(_io__raw_Data, this, this._root)
			if err != nil {
				return err
			}
			this.Data = tmp14
		case Jpeg_Segment_MarkerEnum__Sos:
			tmp15, err := this._io.ReadBytes(int(this.Length - 2))
			if err != nil {
				return err
			}
			tmp15 = tmp15
			this._raw_Data = tmp15
			_io__raw_Data := kaitai.NewStream(bytes.NewReader(this._raw_Data))
			tmp16 := NewJpeg_SegmentSos()
			err = tmp16.Read(_io__raw_Data, this, this._root)
			if err != nil {
				return err
			}
			this.Data = tmp16
		default:
			tmp17, err := this._io.ReadBytes(int(this.Length - 2))
			if err != nil {
				return err
			}
			tmp17 = tmp17
			this._raw_Data = tmp17
		}
	}
	if (this.Marker == Jpeg_Segment_MarkerEnum__Sos) {
		tmp18, err := this._io.ReadBytesFull()
		if err != nil {
			return err
		}
		tmp18 = tmp18
		this.ImageData = tmp18
	}
	return err
}

type Jpeg_SegmentApp0_DensityUnit int
const (
	Jpeg_SegmentApp0_DensityUnit__NoUnits Jpeg_SegmentApp0_DensityUnit = 0
	Jpeg_SegmentApp0_DensityUnit__PixelsPerInch Jpeg_SegmentApp0_DensityUnit = 1
	Jpeg_SegmentApp0_DensityUnit__PixelsPerCm Jpeg_SegmentApp0_DensityUnit = 2
)
var values_Jpeg_SegmentApp0_DensityUnit = map[Jpeg_SegmentApp0_DensityUnit]struct{}{0: {}, 1: {}, 2: {}}
func (v Jpeg_SegmentApp0_DensityUnit) isDefined() bool {
	_, ok := values_Jpeg_SegmentApp0_DensityUnit[v]
	return ok
}
type Jpeg_SegmentApp0 struct {
	Magic string
	VersionMajor uint8
	VersionMinor uint8
	DensityUnits Jpeg_SegmentApp0_DensityUnit
	DensityX uint16
	DensityY uint16
	ThumbnailX uint8
	ThumbnailY uint8
	Thumbnail []byte
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg_Segment
}
func NewJpeg_SegmentApp0() *Jpeg_SegmentApp0 {
	return &Jpeg_SegmentApp0{
	}
}

func (this Jpeg_SegmentApp0) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_SegmentApp0) Read(io *kaitai.Stream, parent *Jpeg_Segment, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp19, err := this._io.ReadBytes(int(5))
	if err != nil {
		return err
	}
	tmp19 = tmp19
	this.Magic = string(tmp19)
	tmp20, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.VersionMajor = tmp20
	tmp21, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.VersionMinor = tmp21
	tmp22, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.DensityUnits = Jpeg_SegmentApp0_DensityUnit(tmp22)
	tmp23, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.DensityX = uint16(tmp23)
	tmp24, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.DensityY = uint16(tmp24)
	tmp25, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.ThumbnailX = tmp25
	tmp26, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.ThumbnailY = tmp26
	tmp27, err := this._io.ReadBytes(int((this.ThumbnailX * this.ThumbnailY) * 3))
	if err != nil {
		return err
	}
	tmp27 = tmp27
	this.Thumbnail = tmp27
	return err
}

/**
 * Horizontal pixel density. Must not be zero.
 */

/**
 * Vertical pixel density. Must not be zero.
 */

/**
 * Horizontal pixel count of the following embedded RGB thumbnail. May be zero.
 */

/**
 * Vertical pixel count of the following embedded RGB thumbnail. May be zero.
 */

/**
 * Uncompressed 24 bit RGB (8 bits per color channel) raster thumbnail data in the order R0, G0, B0, ... Rn, Gn, Bn
 */
type Jpeg_SegmentApp1 struct {
	Magic string
	Body *Jpeg_ExifInJpeg
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg_Segment
}
func NewJpeg_SegmentApp1() *Jpeg_SegmentApp1 {
	return &Jpeg_SegmentApp1{
	}
}

func (this Jpeg_SegmentApp1) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_SegmentApp1) Read(io *kaitai.Stream, parent *Jpeg_Segment, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp28, err := this._io.ReadBytesTerm(0, false, true, true)
	if err != nil {
		return err
	}
	this.Magic = string(tmp28)
	switch (this.Magic) {
	case "Exif":
		tmp29 := NewJpeg_ExifInJpeg()
		err = tmp29.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Body = tmp29
	}
	return err
}
type Jpeg_SegmentSof0 struct {
	BitsPerSample uint8
	ImageHeight uint16
	ImageWidth uint16
	NumComponents uint8
	Components []*Jpeg_SegmentSof0_Component
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg_Segment
}
func NewJpeg_SegmentSof0() *Jpeg_SegmentSof0 {
	return &Jpeg_SegmentSof0{
	}
}

func (this Jpeg_SegmentSof0) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_SegmentSof0) Read(io *kaitai.Stream, parent *Jpeg_Segment, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp30, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.BitsPerSample = tmp30
	tmp31, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.ImageHeight = uint16(tmp31)
	tmp32, err := this._io.ReadU2be()
	if err != nil {
		return err
	}
	this.ImageWidth = uint16(tmp32)
	tmp33, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.NumComponents = tmp33
	for i := 0; i < int(this.NumComponents); i++ {
		_ = i
		tmp34 := NewJpeg_SegmentSof0_Component()
		err = tmp34.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Components = append(this.Components, tmp34)
	}
	return err
}
type Jpeg_SegmentSof0_Component struct {
	Id Jpeg_ComponentId
	SamplingFactors uint8
	QuantizationTableId uint8
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg_SegmentSof0
	_f_samplingX bool
	samplingX int
	_f_samplingY bool
	samplingY int
}
func NewJpeg_SegmentSof0_Component() *Jpeg_SegmentSof0_Component {
	return &Jpeg_SegmentSof0_Component{
	}
}

func (this Jpeg_SegmentSof0_Component) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_SegmentSof0_Component) Read(io *kaitai.Stream, parent *Jpeg_SegmentSof0, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp35, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Id = Jpeg_ComponentId(tmp35)
	tmp36, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.SamplingFactors = tmp36
	tmp37, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.QuantizationTableId = tmp37
	return err
}
func (this *Jpeg_SegmentSof0_Component) SamplingX() (v int, err error) {
	if (this._f_samplingX) {
		return this.samplingX, nil
	}
	this._f_samplingX = true
	this.samplingX = int((this.SamplingFactors & 240) >> 4)
	return this.samplingX, nil
}
func (this *Jpeg_SegmentSof0_Component) SamplingY() (v int, err error) {
	if (this._f_samplingY) {
		return this.samplingY, nil
	}
	this._f_samplingY = true
	this.samplingY = int(this.SamplingFactors & 15)
	return this.samplingY, nil
}

/**
 * Component selector
 */
type Jpeg_SegmentSos struct {
	NumComponents uint8
	Components []*Jpeg_SegmentSos_Component
	StartSpectralSelection uint8
	EndSpectral uint8
	ApprBitPos uint8
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg_Segment
}
func NewJpeg_SegmentSos() *Jpeg_SegmentSos {
	return &Jpeg_SegmentSos{
	}
}

func (this Jpeg_SegmentSos) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_SegmentSos) Read(io *kaitai.Stream, parent *Jpeg_Segment, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp38, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.NumComponents = tmp38
	for i := 0; i < int(this.NumComponents); i++ {
		_ = i
		tmp39 := NewJpeg_SegmentSos_Component()
		err = tmp39.Read(this._io, this, this._root)
		if err != nil {
			return err
		}
		this.Components = append(this.Components, tmp39)
	}
	tmp40, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.StartSpectralSelection = tmp40
	tmp41, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.EndSpectral = tmp41
	tmp42, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.ApprBitPos = tmp42
	return err
}

/**
 * Number of components in scan
 */

/**
 * Scan components specification
 */

/**
 * Start of spectral selection or predictor selection
 */

/**
 * End of spectral selection
 */

/**
 * Successive approximation bit position high + Successive approximation bit position low or point transform
 */
type Jpeg_SegmentSos_Component struct {
	Id Jpeg_ComponentId
	HuffmanTable uint8
	_io *kaitai.Stream
	_root *Jpeg
	_parent *Jpeg_SegmentSos
}
func NewJpeg_SegmentSos_Component() *Jpeg_SegmentSos_Component {
	return &Jpeg_SegmentSos_Component{
	}
}

func (this Jpeg_SegmentSos_Component) IO_() *kaitai.Stream {
	return this._io
}

func (this *Jpeg_SegmentSos_Component) Read(io *kaitai.Stream, parent *Jpeg_SegmentSos, root *Jpeg) (err error) {
	this._io = io
	this._parent = parent
	this._root = root

	tmp43, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.Id = Jpeg_ComponentId(tmp43)
	tmp44, err := this._io.ReadU1()
	if err != nil {
		return err
	}
	this.HuffmanTable = tmp44
	return err
}

/**
 * Scan component selector
 */