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 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)