__all__ = [ 'DevModeA', ] import logging import struct from typing import Final, Optional from ._helpers import BytesReader from ..enums import DevModeFields logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) class DevModeA: """ A DEVMODEA structure, as specified in [MS-OLEDS]. For the purposes of parsing from bytes, if something goes wrong this will evaluate to ``False`` when converting to ``bool``. If no data is prodided, the fields are set to default values. """ PARSE_STRUCT: Final[struct.Struct] = struct.Struct('<32s32s4HI13h14xI4x4I16x') def __init__(self, data: Optional[bytes] = None): self.__valid = data is None # Set default values for fields that may not be initialized. self.__orientation = 0 self.__paperSize = 0 self.__paperLength = 0 self.__paperWidth = 0 self.__scale = 0 self.__copies = 0 self.__defaultSource = 0 self.__printQuality = 0 self.__color = 0 self.__duplex = 0 self.__yResolution = 0 self.__ttOption = 0 self.__collate = 0 self.__nup = 0 self.__icmMethod = 0 self.__icmIntent = 0 self.__mediaType = 0 self.__ditherType = 0 if self.__valid: self.__deviceName = b'\x00' * 32 self.__formName = b'\x00' * 32 self.__specVersion = 0 self.__driverVersion = 0 self.__driverExtra = 0 self.__fields = DevModeFields[0] return reader = BytesReader(data) try: items = reader.readStruct(self.PARSE_STRUCT) self.__deviceName = items[0] self.__formName = items[1] self.__specVersion = items[2] self.__driverVersion = items[3] if items[4] != self.PARSE_STRUCT.size: logger.warn(f'Unexpected `size` field for DevModeA detected ({items[4]})') self.__driverExtra = items[5] self.__fields = DevModeFields(items[6]) # TODO fields specifies if we should read the field or ignore it. if DevModeFields.DM_ORIENTATION in self.__fields: self.__orientation = items[7] if DevModeFields.DM_PAPERSIZE in self.__fields: self.__paperSize = items[8] if DevModeFields.DM_PAPERLENGTH in self.__fields: self.__paperLength = items[9] if DevModeFields.DM_PAPERWIDTH in self.__fields: self.__paperWidth = items[10] if DevModeFields.DM_SCALE in self.__fields: self.__scale = items[11] if DevModeFields.DM_COPIES in self.__fields: self.__copies = items[12] if DevModeFields.DM_DEFAULTSOURCE in self.__fields: self.__defaultSource = items[13] if DevModeFields.DM_PRINTQUALITY in self.__fields: self.__printQuality = items[14] if DevModeFields.DM_COLOR in self.__fields: self.__color = items[15] if DevModeFields.DM_DUPLEX in self.__fields: self.__duplex = items[16] if DevModeFields.DM_YRESOLUTION in self.__fields: self.__yResolution = items[17] if DevModeFields.DM_TTOPTION in self.__fields: self.__ttOption = items[18] if DevModeFields.DM_COLLATE in self.__fields: self.__collate = items[19] if DevModeFields.DM_NUP in self.__fields: self.__nup = items[20] if DevModeFields.DM_ICMMETHOD in self.__fields: self.__icmMethod = items[21] if DevModeFields.DM_ICMINTENT in self.__fields: self.__icmIntent = items[22] if DevModeFields.DM_MEDIATYPE in self.__fields: self.__mediaType = items[23] if DevModeFields.DM_DITHERTYPE in self.__fields: self.__ditherType = items[24] except IOError: return self.__valid = True def __bool__(self) -> bool: return self.__valid def __bytes__(self) -> bytes: return self.toBytes() def toBytes(self) -> bytes: return self.PARSE_STRUCT.pack( self.__deviceName, self.__formName, self.__specVersion, self.__driverVersion, self.PARSE_STRUCT.size, self.__driverExtra, self.__fields, self.__orientation, self.__paperSize, self.__paperLength, self.__paperWidth, self.__scale, self.__copies, self.__defaultSource, self.__printQuality, self.__color, self.__duplex, self.__yResolution, self.__ttOption, self.__collate, self.__nup, self.__icmMethod, self.__icmIntent, self.__mediaType, self.__ditherType, ) @property def collate(self) -> int: return self.__collate @collate.setter def collate(self, val: Optional[int]) -> None: if val is None: self.__collate = 0 if DevModeFields.DM_COLLATE in self.__fields: self.__fields ^= DevModeFields.DM_COLLATE return elif not isinstance(val, int): raise TypeError(':property collate: must be an int or None.') if val < -0x8000: raise ValueError(':property collate: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property collate: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_COLLATE self.__collate = val @property def color(self) -> int: return self.__color @color.setter def color(self, val: Optional[int]) -> None: if val is None: self.__color = 0 if DevModeFields.DM_COLOR in self.__fields: self.__fields ^= DevModeFields.DM_COLOR return elif not isinstance(val, int): raise TypeError(':property color: must be an int or None.') if val < -0x8000: raise ValueError(':property color: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property color: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_COLOR self.__color = val @property def copies(self) -> int: return self.__copies @copies.setter def copies(self, val: Optional[int]) -> None: if val is None: self.__copies = 0 if DevModeFields.DM_COPIES in self.__fields: self.__fields ^= DevModeFields.DM_COPIES return elif not isinstance(val, int): raise TypeError(':property copies: must be an int or None.') if val < -0x8000: raise ValueError(':property copies: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property copies: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_COPIES self.__copies = val @property def defaultSource(self) -> int: return self.__defaultSource @defaultSource.setter def defaultSource(self, val: Optional[int]) -> None: if val is None: self.__defaultSource = 0 if DevModeFields.DM_DEFAULTSOURCE in self.__fields: self.__fields ^= DevModeFields.DM_DEFAULTSOURCE return elif not isinstance(val, int): raise TypeError(':property defaultSource: must be an int or None.') if val < -0x8000: raise ValueError(':property defaultSource: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property defaultSource: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_DEFAULTSOURCE self.__defaultSource = val @property def deviceName(self) -> bytes: """ A 32 byte ANSI string. """ return self.__deviceName @deviceName.setter def deviceName(self, val: bytes) -> None: if not isinstance(val, bytes): raise TypeError(':property deviceName: must be bytes.') if len(val) != 32: raise ValueError(':property deviceName: must be exactly 32 bytes.') self.__deviceName = val @property def ditherType(self) -> int: return self.__ditherType @ditherType.setter def ditherType(self, val: Optional[int]) -> None: if val is None: self.__ditherType = 0 if DevModeFields.DM_DITHERTYPE in self.__fields: self.__fields ^= DevModeFields.DM_DITHERTYPE return elif not isinstance(val, int): raise TypeError(':property ditherType: must be an int or None.') if val < 0: raise ValueError(':property ditherType: must be positive.') if val > 0xFFFFFFFF: raise ValueError(':property ditherType: cannot be greater than 0xFFFFFFFF.') self.__fields |= DevModeFields.DM_DITHERTYPE self.__ditherType = val @property def driverExtra(self) -> int: return self.__driverExtra @driverExtra.setter def driverExtra(self, val: int) -> None: if not isinstance(val, int): raise TypeError(':property driverExtra: must be an int.') if val < 0: raise ValueError(':property driverExtra: must be positive.') if val > 0xFFFF: raise ValueError(':property driverExtra: cannot be greater than 0xFFFF.') self.__driverExtra = val @property def driverVersion(self) -> int: return self.__driverVersion @driverVersion.setter def driverVersion(self, val: int) -> None: if not isinstance(val, int): raise TypeError(':property driverVersion: must be an int or None.') if val < 0: raise ValueError(':property driverVersion: must be positive.') if val > 0xFFFF: raise ValueError(':property driverVersion: cannot be greater than 0xFFFF.') self.__driverVersion = val @property def duplex(self) -> int: return self.__duplex @duplex.setter def duplex(self, val: Optional[int]) -> None: if val is None: self.__duplex = 0 if DevModeFields.DM_DUPLEX in self.__fields: self.__fields ^= DevModeFields.DM_DUPLEX return elif not isinstance(val, int): raise TypeError(':property duplex: must be an int or None.') if val < -0x8000: raise ValueError(':property duplex: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property duplex: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_DUPLEX self.__duplex = val @property def formName(self) -> bytes: """ A 32 byte ANSI string. """ return self.__formName @formName.setter def formName(self, val: bytes) -> None: if not isinstance(val, bytes): raise TypeError(':property formName: must be bytes.') if len(val) != 32: raise ValueError(':property formName: must be exactly 32 bytes.') self.__formName = val @property def icmIntent(self) -> int: return self.__icmIntent @icmIntent.setter def icmIntent(self, val: Optional[int]) -> None: if val is None: self.__icmIntent = 0 if DevModeFields.DM_ICMINTENT in self.__fields: self.__fields ^= DevModeFields.DM_ICMINTENT return elif not isinstance(val, int): raise TypeError(':property icmIntent: must be an int or None.') if val < 0: raise ValueError(':property icmIntent: must be positive.') if val > 0xFFFFFFFF: raise ValueError(':property icmIntent: cannot be greater than 0xFFFFFFFF.') self.__fields |= DevModeFields.DM_ICMINTENT self.__icmIntent = val @property def icmMethod(self) -> int: return self.__icmMethod @icmMethod.setter def icmMethod(self, val: Optional[int]) -> None: if val is None: self.__icmMethod = 0 if DevModeFields.DM_ICMMETHOD in self.__fields: self.__fields ^= DevModeFields.DM_ICMMETHOD return elif not isinstance(val, int): raise TypeError(':property icmMethod: must be an int or None.') if val < 0: raise ValueError(':property icmMethod: must be positive.') if val > 0xFFFFFFFF: raise ValueError(':property icmMethod: cannot be greater than 0xFFFFFFFF.') self.__fields |= DevModeFields.DM_ICMMETHOD self.__icmMethod = val @property def mediaType(self) -> int: return self.__mediaType @mediaType.setter def mediaType(self, val: Optional[int]) -> None: if val is None: self.__mediaType = 0 if DevModeFields.DM_MEDIATYPE in self.__fields: self.__fields ^= DevModeFields.DM_MEDIATYPE return elif not isinstance(val, int): raise TypeError(':property mediaType: must be an int or None.') if val < 0: raise ValueError(':property mediaType: must be positive.') if val > 0xFFFFFFFF: raise ValueError(':property mediaType: cannot be greater than 0xFFFFFFFF.') self.__fields |= DevModeFields.DM_MEDIATYPE self.__mediaType = val @property def nup(self) -> int: return self.__nup @nup.setter def nup(self, val: Optional[int]) -> None: if val is None: self.__nup = 0 if DevModeFields.DM_NUP in self.__fields: self.__fields ^= DevModeFields.DM_NUP return elif not isinstance(val, int): raise TypeError(':property nup: must be an int or None.') if val < 0: raise ValueError(':property nup: must be positive.') if val > 0xFFFFFFFF: raise ValueError(':property nup: cannot be greater than 0xFFFFFFFF.') self.__fields |= DevModeFields.DM_NUP self.__nup = val @property def orientation(self) -> int: return self.__orientation @orientation.setter def orientation(self, val: Optional[int]) -> None: if val is None: self.__orientation = 0 if DevModeFields.DM_ORIENTATION in self.__fields: self.__fields ^= DevModeFields.DM_ORIENTATION return elif not isinstance(val, int): raise TypeError(':property orientation: must be an int or None.') if val < -0x8000: raise ValueError(':property orientation: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property orientation: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_ORIENTATION self.__orientation = val @property def paperLength(self) -> int: return self.__paperLength @paperLength.setter def paperLength(self, val: Optional[int]) -> None: if val is None: self.__paperLength = 0 if DevModeFields.DM_PAPERLENGTH in self.__fields: self.__fields ^= DevModeFields.DM_PAPERLENGTH return elif not isinstance(val, int): raise TypeError(':property paperLength: must be an int or None.') if val < -0x8000: raise ValueError(':property paperLength: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property paperLength: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_PAPERLENGTH self.__paperLength = val @property def paperSize(self) -> int: return self.__paperSize @paperSize.setter def paperSize(self, val: Optional[int]) -> None: if val is None: self.__paperSize = 0 if DevModeFields.DM_PAPERSIZE in self.__fields: self.__fields ^= DevModeFields.DM_PAPERSIZE return elif not isinstance(val, int): raise TypeError(':property paperSize: must be an int or None.') if val < -0x8000: raise ValueError(':property paperSize: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property paperSize: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_PAPERSIZE self.__paperSize = val @property def paperWidth(self) -> int: return self.__paperWidth @paperWidth.setter def paperWidth(self, val: Optional[int]) -> None: if val is None: self.__paperWidth = 0 if DevModeFields.DM_PAPERWIDTH in self.__fields: self.__fields ^= DevModeFields.DM_PAPERWIDTH return elif not isinstance(val, int): raise TypeError(':property paperWidth: must be an int or None.') if val < -0x8000: raise ValueError(':property paperWidth: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property paperWidth: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_PAPERWIDTH self.__paperWidth = val @property def printQuality(self) -> int: return self.__printQuality @printQuality.setter def printQuality(self, val: Optional[int]) -> None: if val is None: self.__printQuality = 0 if DevModeFields.DM_PRINTQUALITY in self.__fields: self.__fields ^= DevModeFields.DM_PRINTQUALITY return elif not isinstance(val, int): raise TypeError(':property printQuality: must be an int or None.') if val < -0x8000: raise ValueError(':property printQuality: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property printQuality: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_PRINTQUALITY self.__printQuality = val @property def scale(self) -> int: return self.__scale @scale.setter def scale(self, val: Optional[int]) -> None: if val is None: self.__scale = 0 if DevModeFields.DM_SCALE in self.__fields: self.__fields ^= DevModeFields.DM_SCALE return elif not isinstance(val, int): raise TypeError(':property scale: must be an int or None.') if val < -0x8000: raise ValueError(':property scale: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property scale: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_SCALE self.__scale = val @property def specVersion(self) -> int: return self.__specVersion @specVersion.setter def specVersion(self, val: int) -> None: if not isinstance(val, int): raise TypeError(':property specVersion: must be an int.') if val < 0: raise ValueError(':property specVersion: must be positive.') if val > 0xFFFF: raise ValueError(':property specVersion: cannot be greater than 0xFFFF.') self.__specVersion = val @property def ttOption(self) -> int: return self.__ttOption @ttOption.setter def ttOption(self, val: Optional[int]) -> None: if val is None: self.__ttOption = 0 if DevModeFields.DM_TTOPTION in self.__fields: self.__fields ^= DevModeFields.DM_TTOPTION return elif not isinstance(val, int): raise TypeError(':property ttOption: must be an int or None.') if val < -0x8000: raise ValueError(':property ttOption: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property ttOption: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_TTOPTION self.__ttOption = val @property def yResolution(self) -> int: return self.__yResolution @yResolution.setter def yResolution(self, val: Optional[int]) -> None: if val is None: self.__yResolution = 0 if DevModeFields.DM_YRESOLUTION in self.__fields: self.__fields ^= DevModeFields.DM_YRESOLUTION return elif not isinstance(val, int): raise TypeError(':property yResolution: must be an int or None.') if val < -0x8000: raise ValueError(':property yResolution: cannot be less than -0x8000.') if val > 0x7FFF: raise ValueError(':property yResolution: cannot be greater than 0x7FFF.') self.__fields |= DevModeFields.DM_YRESOLUTION self.__yResolution = val
Memory