summaryrefslogtreecommitdiff
path: root/vendor/github.com/dsoprea/go-photoshop-info-format/info.go
blob: 7f17fa6c0fca8ffca650c87a0a17c4a65f665332 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package photoshopinfo

import (
	"fmt"
	"io"

	"encoding/binary"

	"github.com/dsoprea/go-logging"
)

var (
	defaultByteOrder = binary.BigEndian
)

// Photoshop30InfoRecord is the data for one parsed Photoshop-info record.
type Photoshop30InfoRecord struct {
	// RecordType is the record-type.
	RecordType string

	// ImageResourceId is the image resource-ID.
	ImageResourceId uint16

	// Name is the name of the record. It is optional and will be an empty-
	// string if not present.
	Name string

	// Data is the raw record data.
	Data []byte
}

// String returns a descriptive string.
func (pir Photoshop30InfoRecord) String() string {
	return fmt.Sprintf("RECORD-TYPE=[%s] IMAGE-RESOURCE-ID=[0x%04x] NAME=[%s] DATA-SIZE=(%d)", pir.RecordType, pir.ImageResourceId, pir.Name, len(pir.Data))
}

// ReadPhotoshop30InfoRecord parses a single photoshop-info record.
func ReadPhotoshop30InfoRecord(r io.Reader) (pir Photoshop30InfoRecord, err error) {
	defer func() {
		if state := recover(); state != nil {
			err = log.Wrap(state.(error))
		}
	}()

	recordType := make([]byte, 4)
	_, err = io.ReadFull(r, recordType)
	if err != nil {
		if err == io.EOF {
			return pir, err
		}

		log.Panic(err)
	}

	// TODO(dustin): Move BigEndian to constant/config.

	irId := uint16(0)
	err = binary.Read(r, defaultByteOrder, &irId)
	log.PanicIf(err)

	nameSize := uint8(0)
	err = binary.Read(r, defaultByteOrder, &nameSize)
	log.PanicIf(err)

	// Add an extra byte if the two length+data size is odd to make the total
	// bytes read even.
	doAddPadding := (1+nameSize)%2 == 1
	if doAddPadding == true {
		nameSize++
	}

	name := make([]byte, nameSize)
	_, err = io.ReadFull(r, name)
	log.PanicIf(err)

	// If the last byte is padding, truncate it.
	if doAddPadding == true {
		name = name[:nameSize-1]
	}

	dataSize := uint32(0)
	err = binary.Read(r, defaultByteOrder, &dataSize)
	log.PanicIf(err)

	data := make([]byte, dataSize+dataSize%2)
	_, err = io.ReadFull(r, data)
	log.PanicIf(err)

	data = data[:dataSize]

	pir = Photoshop30InfoRecord{
		RecordType:      string(recordType),
		ImageResourceId: irId,
		Name:            string(name),
		Data:            data,
	}

	return pir, nil
}

// ReadPhotoshop30Info parses a sequence of photoship-info records from the stream.
func ReadPhotoshop30Info(r io.Reader) (pirIndex map[uint16]Photoshop30InfoRecord, err error) {
	pirIndex = make(map[uint16]Photoshop30InfoRecord)

	for {
		pir, err := ReadPhotoshop30InfoRecord(r)
		if err != nil {
			if err == io.EOF {
				break
			}

			log.Panic(err)
		}

		pirIndex[pir.ImageResourceId] = pir
	}

	return pirIndex, nil
}