pyjpeg 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyjpeg/__init__.py +139 -0
- pyjpeg/app.py +423 -0
- pyjpeg/arithmetic.py +401 -0
- pyjpeg/arithmetic_dct_ac_successive_scan.py +191 -0
- pyjpeg/arithmetic_dct_dc_successive_scan.py +64 -0
- pyjpeg/arithmetic_dct_scan.py +320 -0
- pyjpeg/arithmetic_lossless_scan.py +265 -0
- pyjpeg/arithmetic_scan.py +259 -0
- pyjpeg/com.py +36 -0
- pyjpeg/dac.py +84 -0
- pyjpeg/dct.py +176 -0
- pyjpeg/dht.py +147 -0
- pyjpeg/dnl.py +51 -0
- pyjpeg/dqt.py +94 -0
- pyjpeg/dri.py +51 -0
- pyjpeg/eoi.py +30 -0
- pyjpeg/exp.py +52 -0
- pyjpeg/golomb_scan.py +117 -0
- pyjpeg/huffman.py +299 -0
- pyjpeg/huffman_dct_ac_successive_scan.py +206 -0
- pyjpeg/huffman_dct_dc_successive_scan.py +67 -0
- pyjpeg/huffman_dct_scan.py +311 -0
- pyjpeg/huffman_lossless_scan.py +164 -0
- pyjpeg/huffman_optimize.py +97 -0
- pyjpeg/huffman_scan.py +179 -0
- pyjpeg/huffman_tables.py +368 -0
- pyjpeg/image.py +130 -0
- pyjpeg/io.py +91 -0
- pyjpeg/lossless.py +116 -0
- pyjpeg/ls_scan.py +967 -0
- pyjpeg/lse.py +229 -0
- pyjpeg/marker.py +60 -0
- pyjpeg/quantization_tables.py +134 -0
- pyjpeg/rst.py +34 -0
- pyjpeg/scan.py +68 -0
- pyjpeg/segment.py +6 -0
- pyjpeg/sof.py +269 -0
- pyjpeg/soi.py +31 -0
- pyjpeg/sos.py +152 -0
- pyjpeg/stream.py +494 -0
- pyjpeg-0.1.dist-info/METADATA +30 -0
- pyjpeg-0.1.dist-info/RECORD +44 -0
- pyjpeg-0.1.dist-info/WHEEL +4 -0
- pyjpeg-0.1.dist-info/licenses/LICENSE +165 -0
pyjpeg/__init__.py
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from pyjpeg.app import (
|
|
2
|
+
AdobeColorSpace,
|
|
3
|
+
AdobeHeader,
|
|
4
|
+
ApplicationSpecificData,
|
|
5
|
+
ExifHeader,
|
|
6
|
+
JfifDensity,
|
|
7
|
+
JfifDensityUnit,
|
|
8
|
+
JfifHeader,
|
|
9
|
+
JfifJpegThumbnail,
|
|
10
|
+
JfifPalletizedThumbnail,
|
|
11
|
+
JfifRgbThumbnail,
|
|
12
|
+
SpiffColorSpace,
|
|
13
|
+
SpiffCompressionType,
|
|
14
|
+
SpiffHeader,
|
|
15
|
+
SpiffProfile,
|
|
16
|
+
UnknownApplicationSpecificData,
|
|
17
|
+
)
|
|
18
|
+
from pyjpeg.arithmetic_dct_ac_successive_scan import ArithmeticDCTACSuccessiveScan
|
|
19
|
+
from pyjpeg.arithmetic_dct_dc_successive_scan import ArithmeticDCTDCSuccessiveScan
|
|
20
|
+
from pyjpeg.arithmetic_dct_scan import ArithmeticDCTScan, ArithmeticDCTScanComponent
|
|
21
|
+
from pyjpeg.arithmetic_lossless_scan import (
|
|
22
|
+
ArithmeticLosslessScan,
|
|
23
|
+
ArithmeticLosslessScanComponent,
|
|
24
|
+
)
|
|
25
|
+
from pyjpeg.com import Comment
|
|
26
|
+
from pyjpeg.dac import ArithmeticConditioning, DefineArithmeticConditioning
|
|
27
|
+
from pyjpeg.dct import fdct, idct, unzig_zag, zig_zag
|
|
28
|
+
from pyjpeg.dht import DefineHuffmanTables, HuffmanTable
|
|
29
|
+
from pyjpeg.dnl import DefineNumberOfLines
|
|
30
|
+
from pyjpeg.dqt import DefineQuantizationTables, QuantizationTable
|
|
31
|
+
from pyjpeg.dri import DefineRestartInterval
|
|
32
|
+
from pyjpeg.eoi import EndOfImage
|
|
33
|
+
from pyjpeg.exp import ExpandReferenceComponents
|
|
34
|
+
from pyjpeg.huffman_dct_ac_successive_scan import HuffmanDCTACSuccessiveScan
|
|
35
|
+
from pyjpeg.huffman_dct_dc_successive_scan import HuffmanDCTDCSuccessiveScan
|
|
36
|
+
from pyjpeg.huffman_dct_scan import HuffmanDCTScan, HuffmanDCTScanComponent
|
|
37
|
+
from pyjpeg.huffman_lossless_scan import HuffmanLosslessScan, HuffmanLosslessScanComponent
|
|
38
|
+
from pyjpeg.huffman_optimize import optimize as huffman_optimize
|
|
39
|
+
from pyjpeg.huffman_tables import (
|
|
40
|
+
standard_chrominance_ac_huffman_table,
|
|
41
|
+
standard_chrominance_dc_huffman_table,
|
|
42
|
+
standard_luminance_ac_huffman_table,
|
|
43
|
+
standard_luminance_dc_huffman_table,
|
|
44
|
+
)
|
|
45
|
+
from pyjpeg.image import Component, Image
|
|
46
|
+
from pyjpeg.io import BufferedReader, BufferedWriter, Reader, Writer
|
|
47
|
+
from pyjpeg.ls_scan import LSInterleaveMode, LSScan, LSScanComponent
|
|
48
|
+
from pyjpeg.lse import (
|
|
49
|
+
LSCodingParameters,
|
|
50
|
+
LSMappingTable,
|
|
51
|
+
LSMappingTableContinuation,
|
|
52
|
+
LSOversizeImageDimensions,
|
|
53
|
+
LSPresetParameters,
|
|
54
|
+
LSUnknownPresetParameters,
|
|
55
|
+
)
|
|
56
|
+
from pyjpeg.quantization_tables import (
|
|
57
|
+
standard_chrominance_quantization_table,
|
|
58
|
+
standard_luminance_quantization_table,
|
|
59
|
+
)
|
|
60
|
+
from pyjpeg.rst import Restart
|
|
61
|
+
from pyjpeg.segment import Segment
|
|
62
|
+
from pyjpeg.sof import FrameComponent, FrameType, StartOfFrame
|
|
63
|
+
from pyjpeg.soi import StartOfImage
|
|
64
|
+
from pyjpeg.sos import ScanComponent, StartOfScan
|
|
65
|
+
from pyjpeg.stream import Stream
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"AdobeColorSpace",
|
|
69
|
+
"AdobeHeader",
|
|
70
|
+
"ApplicationSpecificData",
|
|
71
|
+
"ArithmeticConditioning",
|
|
72
|
+
"ArithmeticDCTACSuccessiveScan",
|
|
73
|
+
"ArithmeticDCTDCSuccessiveScan",
|
|
74
|
+
"ArithmeticDCTScan",
|
|
75
|
+
"ArithmeticDCTScanComponent",
|
|
76
|
+
"ArithmeticLosslessScan",
|
|
77
|
+
"ArithmeticLosslessScanComponent",
|
|
78
|
+
"BufferedReader",
|
|
79
|
+
"BufferedWriter",
|
|
80
|
+
"Comment",
|
|
81
|
+
"DefineArithmeticConditioning",
|
|
82
|
+
"DefineHuffmanTables",
|
|
83
|
+
"DefineNumberOfLines",
|
|
84
|
+
"DefineRestartInterval",
|
|
85
|
+
"DefineQuantizationTables",
|
|
86
|
+
"EndOfImage",
|
|
87
|
+
"ExifHeader",
|
|
88
|
+
"ExpandReferenceComponents",
|
|
89
|
+
"FrameComponent",
|
|
90
|
+
"FrameType",
|
|
91
|
+
"HuffmanDCTACSuccessiveScan",
|
|
92
|
+
"HuffmanDCTDCSuccessiveScan",
|
|
93
|
+
"HuffmanDCTScan",
|
|
94
|
+
"HuffmanDCTScanComponent",
|
|
95
|
+
"HuffmanLosslessScan",
|
|
96
|
+
"HuffmanLosslessScanComponent",
|
|
97
|
+
"HuffmanTable",
|
|
98
|
+
"JfifDensity",
|
|
99
|
+
"JfifDensityUnit",
|
|
100
|
+
"JfifHeader",
|
|
101
|
+
"JfifJpegThumbnail",
|
|
102
|
+
"JfifPalletizedThumbnail",
|
|
103
|
+
"JfifRgbThumbnail",
|
|
104
|
+
"LSCodingParameters",
|
|
105
|
+
"LSInterleaveMode",
|
|
106
|
+
"LSMappingTable",
|
|
107
|
+
"LSMappingTableContinuation",
|
|
108
|
+
"LSOversizeImageDimensions",
|
|
109
|
+
"LSPresetParameters",
|
|
110
|
+
"LSScan",
|
|
111
|
+
"LSScanComponent",
|
|
112
|
+
"LSUnknownPresetParameters",
|
|
113
|
+
"QuantizationTable",
|
|
114
|
+
"Reader",
|
|
115
|
+
"Restart",
|
|
116
|
+
"Segment",
|
|
117
|
+
"ScanComponent",
|
|
118
|
+
"SpiffColorSpace",
|
|
119
|
+
"SpiffCompressionType",
|
|
120
|
+
"SpiffHeader",
|
|
121
|
+
"SpiffProfile",
|
|
122
|
+
"StartOfFrame",
|
|
123
|
+
"StartOfImage",
|
|
124
|
+
"StartOfScan",
|
|
125
|
+
"Stream",
|
|
126
|
+
"UnknownApplicationSpecificData",
|
|
127
|
+
"Writer",
|
|
128
|
+
"fdct",
|
|
129
|
+
"huffman_optimize",
|
|
130
|
+
"idct",
|
|
131
|
+
"standard_chrominance_ac_huffman_table",
|
|
132
|
+
"standard_chrominance_dc_huffman_table",
|
|
133
|
+
"standard_chrominance_quantization_table",
|
|
134
|
+
"standard_luminance_ac_huffman_table",
|
|
135
|
+
"standard_luminance_dc_huffman_table",
|
|
136
|
+
"standard_luminance_quantization_table",
|
|
137
|
+
"unzig_zag",
|
|
138
|
+
"zig_zag",
|
|
139
|
+
]
|
pyjpeg/app.py
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import pyjpeg.marker
|
|
2
|
+
import pyjpeg.segment
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ApplicationSpecificData(pyjpeg.segment.Segment):
|
|
6
|
+
def __init__(self, n: int) -> None:
|
|
7
|
+
assert n >= 0 and n <= 15
|
|
8
|
+
self.n = n
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def read(cls, reader: pyjpeg.io.Reader) -> ApplicationSpecificData:
|
|
12
|
+
marker = reader.read_marker()
|
|
13
|
+
assert (
|
|
14
|
+
marker >= pyjpeg.marker.Marker.APP0 and marker <= pyjpeg.marker.Marker.APP15
|
|
15
|
+
)
|
|
16
|
+
length = reader.read_u16()
|
|
17
|
+
assert length > 2
|
|
18
|
+
|
|
19
|
+
def check_extension(extension_marker: int, signature: bytes) -> bool:
|
|
20
|
+
if marker != extension_marker:
|
|
21
|
+
return False
|
|
22
|
+
for i, byte in enumerate(signature):
|
|
23
|
+
if reader.peek_u8(i) != byte:
|
|
24
|
+
return False
|
|
25
|
+
reader.read(len(signature))
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
if check_extension(pyjpeg.marker.Marker.APP0, b"JFIF\x00"):
|
|
29
|
+
assert length >= 16
|
|
30
|
+
version_major = reader.read_u8()
|
|
31
|
+
version_minor = reader.read_u8()
|
|
32
|
+
density_unit = reader.read_u8()
|
|
33
|
+
density_x = reader.read_u16()
|
|
34
|
+
density_y = reader.read_u16()
|
|
35
|
+
thumbnail_width = reader.read_u8()
|
|
36
|
+
thumbnail_height = reader.read_u8()
|
|
37
|
+
thumbnail_length = thumbnail_width * thumbnail_height * 3
|
|
38
|
+
assert length == 16 + thumbnail_length
|
|
39
|
+
thumbnail_data = []
|
|
40
|
+
for _ in range(thumbnail_length):
|
|
41
|
+
thumbnail_data.append(reader.read_u8())
|
|
42
|
+
return JfifHeader(
|
|
43
|
+
version=(version_major, version_minor),
|
|
44
|
+
density=JfifDensity(density_unit, density_x, density_y),
|
|
45
|
+
thumbnail_size=(thumbnail_width, thumbnail_height),
|
|
46
|
+
thumbnail_data=thumbnail_data,
|
|
47
|
+
)
|
|
48
|
+
elif check_extension(pyjpeg.marker.Marker.APP0, b"JFXX\x00"):
|
|
49
|
+
assert length >= 8
|
|
50
|
+
thumbnail_format = reader.read_u8()
|
|
51
|
+
if thumbnail_format == 0x10:
|
|
52
|
+
jpeg_thumbnail_data = reader.read(length - 8)
|
|
53
|
+
return JfifJpegThumbnail(
|
|
54
|
+
jpeg_thumbnail_data,
|
|
55
|
+
)
|
|
56
|
+
elif thumbnail_format == 0x11:
|
|
57
|
+
assert length >= 778
|
|
58
|
+
thumbnail_width = reader.read_u8()
|
|
59
|
+
thumbnail_height = reader.read_u8()
|
|
60
|
+
palette = []
|
|
61
|
+
for _ in range(768):
|
|
62
|
+
palette.append(reader.read_u8())
|
|
63
|
+
thumbnail_length = thumbnail_width * thumbnail_height
|
|
64
|
+
assert length == 778 + thumbnail_length
|
|
65
|
+
thumbnail_data = []
|
|
66
|
+
for _ in range(thumbnail_length):
|
|
67
|
+
thumbnail_data.append(reader.read_u8())
|
|
68
|
+
return JfifPalletizedThumbnail(
|
|
69
|
+
thumbnail_width,
|
|
70
|
+
thumbnail_height,
|
|
71
|
+
palette,
|
|
72
|
+
thumbnail_data,
|
|
73
|
+
)
|
|
74
|
+
elif thumbnail_format == 0x12:
|
|
75
|
+
assert length >= 10
|
|
76
|
+
thumbnail_width = reader.read_u8()
|
|
77
|
+
thumbnail_height = reader.read_u8()
|
|
78
|
+
thumbnail_length = thumbnail_width * thumbnail_height * 3
|
|
79
|
+
assert length == 10 + thumbnail_length
|
|
80
|
+
thumbnail_data = []
|
|
81
|
+
for _ in range(thumbnail_length):
|
|
82
|
+
thumbnail_data.append(reader.read_u8())
|
|
83
|
+
return JfifRgbThumbnail(
|
|
84
|
+
thumbnail_width,
|
|
85
|
+
thumbnail_height,
|
|
86
|
+
thumbnail_data,
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
assert False
|
|
90
|
+
elif check_extension(pyjpeg.marker.Marker.APP1, b"Exif\x00\x00"):
|
|
91
|
+
return ExifHeader(reader.read(length - 8))
|
|
92
|
+
elif check_extension(pyjpeg.marker.Marker.APP8, b"SPIFF\x00"):
|
|
93
|
+
assert length == 32
|
|
94
|
+
version = reader.read_u16()
|
|
95
|
+
version_major = version >> 8
|
|
96
|
+
version_minor = version & 0xFF
|
|
97
|
+
assert version_major == 1
|
|
98
|
+
profile = reader.read_u8()
|
|
99
|
+
number_of_components = reader.read_u8()
|
|
100
|
+
height = reader.read_u32()
|
|
101
|
+
width = reader.read_u32()
|
|
102
|
+
color_space = reader.read_u8()
|
|
103
|
+
bits_per_sample = reader.read_u8()
|
|
104
|
+
compression_type = reader.read_u8()
|
|
105
|
+
resolution_units = reader.read_u8()
|
|
106
|
+
horizontal_resolution = reader.read_u32()
|
|
107
|
+
vertical_resoution = reader.read_u32()
|
|
108
|
+
return SpiffHeader(
|
|
109
|
+
version=(version_major, version_minor),
|
|
110
|
+
profile=profile,
|
|
111
|
+
number_of_components=number_of_components,
|
|
112
|
+
height=height,
|
|
113
|
+
width=width,
|
|
114
|
+
color_space=color_space,
|
|
115
|
+
bits_per_sample=bits_per_sample,
|
|
116
|
+
compression_type=compression_type,
|
|
117
|
+
resolution_units=resolution_units,
|
|
118
|
+
horizontal_resolution=horizontal_resolution,
|
|
119
|
+
vertical_resoution=vertical_resoution,
|
|
120
|
+
)
|
|
121
|
+
elif check_extension(pyjpeg.marker.Marker.APP14, b"Adobe"):
|
|
122
|
+
assert length == 14
|
|
123
|
+
version = reader.read_u16()
|
|
124
|
+
assert version in (100, 101)
|
|
125
|
+
flags0 = reader.read_u16()
|
|
126
|
+
flags1 = reader.read_u16()
|
|
127
|
+
color_space = reader.read_u8()
|
|
128
|
+
return AdobeHeader(
|
|
129
|
+
version=version, flags0=flags0, flags1=flags1, color_space=color_space
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
data = reader.read(length - 2)
|
|
133
|
+
return UnknownApplicationSpecificData(
|
|
134
|
+
marker - pyjpeg.marker.Marker.APP0, data
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class JfifDensityUnit:
|
|
139
|
+
ASPECT_RATIO = 0
|
|
140
|
+
DPI = 1
|
|
141
|
+
DPCM = 2
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class JfifDensity:
|
|
145
|
+
def __init__(self, unit: int = 0, x: int = 0, y: int = 0) -> None:
|
|
146
|
+
self.unit = unit
|
|
147
|
+
self.x = x
|
|
148
|
+
self.y = y
|
|
149
|
+
|
|
150
|
+
@classmethod
|
|
151
|
+
def aspect_ratio(cls, x: int, y: int) -> JfifDensity:
|
|
152
|
+
return cls(JfifDensityUnit.ASPECT_RATIO, x, y)
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def dpi(cls, x: int, y: int) -> JfifDensity:
|
|
156
|
+
return cls(JfifDensityUnit.DPI, x, y)
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def dpcm(cls, x: int, y: int) -> JfifDensity:
|
|
160
|
+
return cls(JfifDensityUnit.DPCM, x, y)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class JfifHeader(ApplicationSpecificData):
|
|
164
|
+
def __init__(
|
|
165
|
+
self,
|
|
166
|
+
version: tuple[int, int] = (1, 2),
|
|
167
|
+
density: JfifDensity = JfifDensity.aspect_ratio(1, 1),
|
|
168
|
+
thumbnail_size: tuple[int, int] = (0, 0),
|
|
169
|
+
thumbnail_data: list[int] = [],
|
|
170
|
+
) -> None:
|
|
171
|
+
super().__init__(0)
|
|
172
|
+
self.version = version
|
|
173
|
+
self.density = density
|
|
174
|
+
self.thumbnail_size = thumbnail_size
|
|
175
|
+
self.thumbnail_data = thumbnail_data
|
|
176
|
+
|
|
177
|
+
def write(self, writer: pyjpeg.io.Writer) -> None:
|
|
178
|
+
writer.write_marker(pyjpeg.marker.Marker.APP0)
|
|
179
|
+
writer.write_u16(16 + len(self.thumbnail_data))
|
|
180
|
+
writer.write(b"JFIF\x00")
|
|
181
|
+
writer.write_u8(self.version[0])
|
|
182
|
+
writer.write_u8(self.version[1])
|
|
183
|
+
writer.write_u8(self.density.unit)
|
|
184
|
+
writer.write_u16(self.density.x)
|
|
185
|
+
writer.write_u16(self.density.y)
|
|
186
|
+
writer.write_u8(self.thumbnail_size[0])
|
|
187
|
+
writer.write_u8(self.thumbnail_size[1])
|
|
188
|
+
for p in range(len(self.thumbnail_data)):
|
|
189
|
+
writer.write_u8(p)
|
|
190
|
+
|
|
191
|
+
def __repr__(self) -> str:
|
|
192
|
+
return f"JfifHeader(version={self.version}, density={self.density}, thumbnail_size={self.thumbnail_size}, thumbnail_data={self.thumbnail_data!r})"
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
class JfifJpegThumbnail(ApplicationSpecificData):
|
|
196
|
+
def __init__(self, data: bytes) -> None:
|
|
197
|
+
super().__init__(0)
|
|
198
|
+
self.data = data
|
|
199
|
+
|
|
200
|
+
def write(self, writer: pyjpeg.io.Writer) -> None:
|
|
201
|
+
writer.write_marker(pyjpeg.marker.Marker.APP0)
|
|
202
|
+
writer.write_u16(8 + len(self.data))
|
|
203
|
+
writer.write(b"JFXX\x00")
|
|
204
|
+
writer.write_u8(0x10)
|
|
205
|
+
writer.write(self.data)
|
|
206
|
+
|
|
207
|
+
def __repr__(self) -> str:
|
|
208
|
+
return f"JfifJpegThumbnail({self.data!r})"
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class JfifPalletizedThumbnail(ApplicationSpecificData):
|
|
212
|
+
def __init__(
|
|
213
|
+
self, width: int, height: int, palette: list[int], data: list[int]
|
|
214
|
+
) -> None:
|
|
215
|
+
super().__init__(0)
|
|
216
|
+
self.width = width
|
|
217
|
+
self.height = height
|
|
218
|
+
self.palette = palette
|
|
219
|
+
self.data = data
|
|
220
|
+
|
|
221
|
+
def write(self, writer: pyjpeg.io.Writer) -> None:
|
|
222
|
+
writer.write_marker(pyjpeg.marker.Marker.APP0)
|
|
223
|
+
writer.write_u16(10 + len(self.palette) + len(self.data))
|
|
224
|
+
writer.write(b"JFXX\x00")
|
|
225
|
+
writer.write_u8(0x11)
|
|
226
|
+
writer.write_u8(self.width)
|
|
227
|
+
writer.write_u8(self.height)
|
|
228
|
+
for c in self.palette:
|
|
229
|
+
writer.write_u8(c)
|
|
230
|
+
for p in self.data:
|
|
231
|
+
writer.write_u8(p)
|
|
232
|
+
|
|
233
|
+
def __repr__(self) -> str:
|
|
234
|
+
return f"JfifPalletizedThumbnail({self.width}, {self.height}, {self.data})"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class JfifRgbThumbnail(ApplicationSpecificData):
|
|
238
|
+
def __init__(self, width: int, height: int, data: list[int]) -> None:
|
|
239
|
+
super().__init__(0)
|
|
240
|
+
self.width = width
|
|
241
|
+
self.height = height
|
|
242
|
+
self.data = data
|
|
243
|
+
|
|
244
|
+
def write(self, writer: pyjpeg.io.Writer) -> None:
|
|
245
|
+
writer.write_marker(pyjpeg.marker.Marker.APP0)
|
|
246
|
+
writer.write_u16(10 + len(self.data))
|
|
247
|
+
writer.write(b"JFXX\x00")
|
|
248
|
+
writer.write_u8(0x12)
|
|
249
|
+
writer.write_u8(self.width)
|
|
250
|
+
writer.write_u8(self.height)
|
|
251
|
+
for p in self.data:
|
|
252
|
+
writer.write_u8(p)
|
|
253
|
+
|
|
254
|
+
def __repr__(self) -> str:
|
|
255
|
+
return f"JfifRgbThumbnail({self.width}, {self.height}, {self.data})"
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class ExifHeader(ApplicationSpecificData):
|
|
259
|
+
def __init__(self, data: bytes) -> None:
|
|
260
|
+
super().__init__(1)
|
|
261
|
+
self.data = data
|
|
262
|
+
|
|
263
|
+
def write(self, writer: pyjpeg.io.Writer) -> None:
|
|
264
|
+
writer.write_marker(pyjpeg.marker.Marker.APP1)
|
|
265
|
+
writer.write_u16(len(self.data) + 8)
|
|
266
|
+
writer.write(b"Exif\x00\x00")
|
|
267
|
+
writer.write(self.data)
|
|
268
|
+
|
|
269
|
+
def __repr__(self) -> str:
|
|
270
|
+
return f"ExifHeader(data={self.data!r})"
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class SpiffProfile:
|
|
274
|
+
NONE = 0
|
|
275
|
+
CONTINUOUS_TONE = 1
|
|
276
|
+
CONTINUOUS_TONE_PROGRESSIVE = 2
|
|
277
|
+
BI_LEVEL_FACSIMILE = 3
|
|
278
|
+
CONTINUOUS_TONE_FACSIMILE = 4
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class SpiffColorSpace:
|
|
282
|
+
BI_LEVEL_BLACK = 0
|
|
283
|
+
Y_CB_CR_1 = 1
|
|
284
|
+
OTHER = 2
|
|
285
|
+
Y_CB_CR_2 = 3
|
|
286
|
+
Y_CB_CR_3 = 4
|
|
287
|
+
GRAYSCALE = 8
|
|
288
|
+
PHOTO_YCC = 9
|
|
289
|
+
RGB = 10
|
|
290
|
+
CMY = 11
|
|
291
|
+
CMYK = 12
|
|
292
|
+
YCCK = 13
|
|
293
|
+
CIELAB = 14
|
|
294
|
+
BI_LEVEL_WHITE = 1
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
class SpiffCompressionType:
|
|
298
|
+
UNCOMPRESSED = 0
|
|
299
|
+
MODIFIED_HUFFMAN = 1
|
|
300
|
+
MODIFIED_READ = 2
|
|
301
|
+
MODIFIED_MODIFIED_READ = 3
|
|
302
|
+
JBIG = 4
|
|
303
|
+
JPEG = 5
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class SpiffHeader(ApplicationSpecificData):
|
|
307
|
+
def __init__(
|
|
308
|
+
self,
|
|
309
|
+
version: tuple[int, int] = (1, 0),
|
|
310
|
+
profile: int = SpiffProfile.NONE,
|
|
311
|
+
number_of_components: int = 1,
|
|
312
|
+
height: int = 0,
|
|
313
|
+
width: int = 0,
|
|
314
|
+
color_space: int = SpiffColorSpace.RGB,
|
|
315
|
+
bits_per_sample: int = 0,
|
|
316
|
+
compression_type: int = SpiffCompressionType.JPEG,
|
|
317
|
+
resolution_units: int = 0,
|
|
318
|
+
vertical_resoution: int = 1,
|
|
319
|
+
horizontal_resolution: int = 1,
|
|
320
|
+
) -> None:
|
|
321
|
+
super().__init__(8)
|
|
322
|
+
self.version = version
|
|
323
|
+
self.profile = profile
|
|
324
|
+
self.number_of_components = number_of_components
|
|
325
|
+
self.height = height
|
|
326
|
+
self.width = width
|
|
327
|
+
self.color_space = color_space
|
|
328
|
+
self.bits_per_sample = bits_per_sample
|
|
329
|
+
self.compression_type = compression_type
|
|
330
|
+
self.resolution_units = resolution_units
|
|
331
|
+
self.vertical_resoution = vertical_resoution
|
|
332
|
+
self.horizontal_resolution = horizontal_resolution
|
|
333
|
+
|
|
334
|
+
def write(self, writer: pyjpeg.io.Writer) -> None:
|
|
335
|
+
writer.write_marker(pyjpeg.marker.Marker.APP8)
|
|
336
|
+
writer.write_u16(32)
|
|
337
|
+
writer.write(b"SPIFF\x00")
|
|
338
|
+
writer.write_u16(self.version[0] << 8 | self.version[1])
|
|
339
|
+
writer.write_u8(self.profile)
|
|
340
|
+
writer.write_u8(self.number_of_components)
|
|
341
|
+
writer.write_u32(self.height)
|
|
342
|
+
writer.write_u32(self.width)
|
|
343
|
+
writer.write_u8(self.color_space)
|
|
344
|
+
writer.write_u8(self.bits_per_sample)
|
|
345
|
+
writer.write_u8(self.compression_type)
|
|
346
|
+
writer.write_u8(self.resolution_units)
|
|
347
|
+
writer.write_u32(self.vertical_resoution)
|
|
348
|
+
writer.write_u32(self.horizontal_resolution)
|
|
349
|
+
|
|
350
|
+
def __repr__(self) -> str:
|
|
351
|
+
return f"SpiffHeader(version={self.version}, profile={self.profile}, number_of_components={self.number_of_components}, height={self.height}, width={self.width}, color_space={self.color_space}, bits_per_sample={self.bits_per_sample}, compression_type={self.compression_type}, resolution_units={self.resolution_units}, vertical_resoution={self.vertical_resoution}, horizontal_resolution={self.horizontal_resolution})"
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class AdobeColorSpace:
|
|
355
|
+
RGB_OR_CMYK = 0
|
|
356
|
+
Y_CB_CR = 1
|
|
357
|
+
Y_CB_CR_K = 2
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
class AdobeHeader(ApplicationSpecificData):
|
|
361
|
+
def __init__(
|
|
362
|
+
self,
|
|
363
|
+
version: int = 101,
|
|
364
|
+
flags0: int = 0,
|
|
365
|
+
flags1: int = 0,
|
|
366
|
+
color_space: int = AdobeColorSpace.Y_CB_CR,
|
|
367
|
+
) -> None:
|
|
368
|
+
super().__init__(14)
|
|
369
|
+
self.version = version
|
|
370
|
+
self.flags0 = flags0
|
|
371
|
+
self.flags1 = flags1
|
|
372
|
+
self.color_space = color_space
|
|
373
|
+
|
|
374
|
+
def write(self, writer: pyjpeg.io.Writer) -> None:
|
|
375
|
+
writer.write_marker(pyjpeg.marker.Marker.APP14)
|
|
376
|
+
writer.write_u16(14)
|
|
377
|
+
writer.write(b"Adobe")
|
|
378
|
+
writer.write_u16(self.version)
|
|
379
|
+
writer.write_u16(self.flags0)
|
|
380
|
+
writer.write_u16(self.flags1)
|
|
381
|
+
writer.write_u8(self.color_space)
|
|
382
|
+
|
|
383
|
+
def __eq__(self, other: object) -> bool:
|
|
384
|
+
return (
|
|
385
|
+
isinstance(other, AdobeHeader)
|
|
386
|
+
and other.version == self.version
|
|
387
|
+
and other.flags0 == self.flags0
|
|
388
|
+
and other.flags1 == self.flags1
|
|
389
|
+
and other.color_space == self.color_space
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
def __repr__(self) -> str:
|
|
393
|
+
return f"AdobeHeader(version={self.version}, flags0={self.flags0}, flags1={self.flags1}, color_space={self.color_space})"
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
class UnknownApplicationSpecificData(ApplicationSpecificData):
|
|
397
|
+
def __init__(self, n: int, data: bytes) -> None:
|
|
398
|
+
super().__init__(n)
|
|
399
|
+
self.data = data
|
|
400
|
+
|
|
401
|
+
def __eq__(self, other: object) -> bool:
|
|
402
|
+
return (
|
|
403
|
+
isinstance(other, UnknownApplicationSpecificData)
|
|
404
|
+
and other.n == self.n
|
|
405
|
+
and other.data == self.data
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
def __repr__(self) -> str:
|
|
409
|
+
return f"UnknownApplicationSpecificData({self.n}, {self.data!r})"
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
if __name__ == "__main__":
|
|
413
|
+
writer = pyjpeg.io.BufferedWriter()
|
|
414
|
+
JfifHeader().write(writer)
|
|
415
|
+
assert (
|
|
416
|
+
writer.data == b"\xff\xe0\x00\x10JFIF\x00\x01\x02\x00\x00\x01\x00\x01\x00\x00"
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
reader = pyjpeg.io.BufferedReader(writer.data)
|
|
420
|
+
app = ApplicationSpecificData.read(reader)
|
|
421
|
+
assert isinstance(app, JfifHeader)
|
|
422
|
+
assert app.n == 0
|
|
423
|
+
assert app.version == (1, 2)
|