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:
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.
// 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
*/