def device_capture(device_type, manufacturer=None, sensor=None, child_elements=None): """ Returns either the MIX ScannerCapture or the DigitalCameraCapture element depending on the device_type. :device_type: The type of capture device, e.g. 'scanner' or 'camera' :manufacturer: The manufacturer of the capture device as a string :sensor: The type of image sensor of the capture device as a string :child_elements: Child elements as a list """ prefixes = {'scanner': 'scanner', 'camera': 'digitalCamera'} if device_type not in prefixes: raise ValueError('Invalid value. Only "scanner" or "camera" are ' 'valid device types.') if child_elements is None: child_elements = [] container = _element('capture', prefix=prefixes[device_type][0].capitalize() + prefixes[device_type][1:]) if manufacturer: manufacturer_el = _element('manufacturer', prefix=prefixes[device_type]) manufacturer_el.text = manufacturer child_elements.append(manufacturer_el) if sensor and device_type == 'scanner': if sensor in SCANNER_SENSOR_TYPES: sensor_el = _element('scannerSensor') sensor_el.text = sensor child_elements.append(sensor_el) else: raise RestrictedElementError(sensor, 'scannerSensor', SCANNER_SENSOR_TYPES) if sensor and device_type == 'camera': if sensor in CAMERA_SENSOR_TYPES: sensor_el = _element('cameraSensor') sensor_el.text = sensor child_elements.append(sensor_el) else: raise RestrictedElementError(sensor, 'cameraSensor', CAMERA_SENSOR_TYPES) if device_type == 'scanner': child_elements.sort(key=scanner_capture_order) if device_type == 'camera': child_elements.sort(key=camera_capture_order) for element in child_elements: container.append(element) return container
def spatial_metrics(plane=None, unit=None, x_sampling=None, y_sampling=None): """ Returns the MIX SpatialMetrics element. :plane: The sampling frequency plane as a string :unit: The sampling frequency unit as a string :x_sampling: The y sampling frequency as a list (or integer) :y_sampling: The x sampling frequency as a list (or integer) Returns the following ElementTree structure:: <mix:SpatialMetrics> <mix:samplingFrequencyPlane> object plane </mix:samplingFrequencyPlane> <mix:samplingFrequencyUnit>cm</mix:samplingFrequencyUnit> <mix:xSamplingFrequency> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:xSamplingFrequency> <mix:ySamplingFrequency> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:ySamplingFrequency> </mix:SpatialMetrics> """ container = _element('SpatialMetrics') if plane: plane_el = _subelement(container, 'samplingFrequencyPlane') if plane in SAMPLING_FREQUENCY_PLANES: plane_el.text = plane else: raise RestrictedElementError(plane, 'samplingFrequencyPlane', SAMPLING_FREQUENCY_PLANES) if unit: unit_el = _subelement(container, 'samplingFrequencyUnit') if unit in SAMPLING_FREQUENCY_UNITS: unit_el.text = unit else: raise RestrictedElementError(unit, 'samplingFrequencyUnit', SAMPLING_FREQUENCY_UNITS) if x_sampling: _rationaltype_element('xSamplingFrequency', x_sampling, parent=container) if y_sampling: _rationaltype_element('ySamplingFrequency', y_sampling, parent=container) return container
def djvu(djvu_format=None): """ Returns the MIX Djvu element. Djvu format supports only a specific set of types. :djvu_format: The DjVu file format as a string Returns the following ElementTree structure:: <mix:Djvu> <mix:djvuFormat>indirect</mix:djvuFormat> </mix:Djvu> """ container = _element('Djvu') if djvu_format: if djvu_format in DJVU_FORMATS: djvu_format_el = _subelement(container, 'djvuFormat') djvu_format_el.text = djvu_format else: raise RestrictedElementError(djvu_format, 'djvuFormat', DJVU_FORMATS) return container
def gray_response(curves=None, unit=None): """ Returns the MIX GrayResponse element. :curves: The optical density of pixels as a list (of integers) :unit: The precision recorded in grayResponseCurve Returns the following ElementTree structure:: <mix:GrayResponse> <mix:grayResponseCurve>10</mix:grayResponseCurve> <mix:grayResponseUnit> Number represents tenths of a unit </mix:grayResponseUnit> </mix:GrayResponse> """ container = _element('GrayResponse') if curves: curves = _ensure_list(curves) for item in curves: curve_el = _subelement(container, 'grayResponseCurve') curve_el.text = six.text_type(item) if unit: if unit in GRAY_RESPONSE_UNITS: unit_el = _subelement(container, 'grayResponseUnit') unit_el.text = unit else: raise RestrictedElementError(unit, 'grayResponseUnit', GRAY_RESPONSE_UNITS) return container
def bits_per_sample(sample_values=None, sample_unit=None): """ Returns the MIX BitsPerSample element. :sample_values: The bits per sample values as a list :sample_unit: The bits per sample unit as a string Returns the following ElementTree structure:: <mix:BitsPerSample> <mix:bitsPerSampleValue>8</mix:bitsPerSampleValue> <mix:bitsPerSampleUnit>integer</mix:bitsPerSampleUnit> </mix:BitsPerSample> """ container = _element('BitsPerSample') if sample_values: sample_values = _ensure_list(sample_values) for item in sample_values: value_el = _subelement(container, 'bitsPerSampleValue') value_el.text = six.text_type(item) if sample_unit: if sample_unit in BITS_PER_SAMPLE_UNITS: unit_el = _subelement(container, 'bitsPerSampleUnit') unit_el.text = sample_unit else: raise RestrictedElementError(sample_unit, 'bitsPerSampleUnit', BITS_PER_SAMPLE_UNITS) return container
def target_data(target_types=None, external_targets=None, performance_data=None, child_elements=None): """ Returns MIX TargetData element. :target_types: The target types as a list (or string) :external_targets: The locations of external targets as a list :performance_data: The location of performance data as a string :child_elements: Child elements as a list Returns the following ElementTree structure:: <mix:TargetData> <mix:targetType>internal</mix:targetType> <mix:TargetID/> <mix:externalTarget>http://foo</mix:externalTarget> <mix:performanceData>http://foo</mix:performanceData> </mix:TargetData> """ container = _element('TargetData') if child_elements is None: child_elements = [] if target_types: target_types = _ensure_list(target_types) for item in target_types: if item in TARGET_TYPES: type_el = _element('targetType') type_el.text = item child_elements.append(type_el) else: raise RestrictedElementError(item, 'targetType', TARGET_TYPES) if external_targets: external_targets = _ensure_list(external_targets) for item in external_targets: target_el = _element('externalTarget') target_el.text = item child_elements.append(target_el) if performance_data: performance_data = _ensure_list(performance_data) for item in performance_data: data_el = _element('performanceData') data_el.text = item child_elements.append(data_el) child_elements.sort(key=target_data_order) for element in child_elements: container.append(element) return container
def color_encoding(samples_pixel=None, extra_samples=None, child_elements=None): """ Returns the MIX ImageColorEncoding element. :samples_pixel: The number of samples per pixel as an integer :extra_samples: The types of extra samples as a list :child_elements: Child elements as a list Returns the following sorted ElementTree structure:: <mix:ImageColorEncoding> <mix:BitsPerSample/> <mix:samplesPerPixel>3</mix:samplesPerPixel> <mix:extraSamples>unspecified data</mix:extraSamples> <mix:Colormap/> <mix:GrayResponse/> <mix:WhitePoint/> <mix:PrimaryChromaticities/> </mix:ImageColorEncoding> """ container = _element('ImageColorEncoding') if child_elements is None: child_elements = [] if samples_pixel: pixel_el = _element('samplesPerPixel') pixel_el.text = six.text_type(samples_pixel) child_elements.append(pixel_el) if extra_samples: extra_samples = _ensure_list(extra_samples) for item in extra_samples: if item in EXTRA_SAMPLES_TYPES: samples_el = _element('extraSamples') samples_el.text = item child_elements.append(samples_el) else: raise RestrictedElementError(item, 'extraSamples', EXTRA_SAMPLES_TYPES) child_elements.sort(key=color_encoding_order) for element in child_elements: container.append(element) return container
def component(c_photometric_interpretation=None, footroom=None, headroom=None): """ Returns MIX Component element. :c_photometric_interpretation: The component photometric interpretation type as a string :footroom: The footroom as a list (or integer) :headroom: The headroom as a list (or integer) Returns the following ElementTree structure:: <mix:Component> <mix:componentPhotometricInterpretation> R </mix:componentPhotometricInterpretation> <mix:footroom> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:footroom> <mix:headroom> <mix:numerator>20</mix:numerator> <mix:denominator>1</mix:denominator> </mix:headroom> </mix:Component> """ container = _element('Component') if c_photometric_interpretation: if c_photometric_interpretation in COMPONENT_INTERPRETATION_TYPES: cpi_el = _subelement(container, 'componentPhotometricInterpretation') cpi_el.text = c_photometric_interpretation else: raise RestrictedElementError(c_photometric_interpretation, 'componentPhotometricInterpretation', COMPONENT_INTERPRETATION_TYPES) if footroom: _rationaltype_element('footroom', footroom, parent=container) if headroom: _rationaltype_element('headroom', headroom, parent=container) return container
def image_capture_metadata(orientation=None, methodology=None, child_elements=None): """ Returns the MIX ImageCaptureMetadata element. :orientation: The image orientation as a string :methodology: The digitization methodology as a string :child_elements: Child elements as a list Returns the following sorted ElementTree structure:: <mix:ImageCaptureMetadata> {{ Child elements }} <mix:orientation>unknown</mix:orientation> <mix:methodology>unknown</mix:methodology> </mix:ImageCaptureMetadata> """ if child_elements is None: child_elements = [] container = _element('ImageCaptureMetadata') if orientation: if orientation in ORIENTATION_TYPES: orientation_el = _element('orientation') orientation_el.text = orientation child_elements.append(orientation_el) else: raise RestrictedElementError(orientation, 'orientation', ORIENTATION_TYPES) if methodology: methodology_el = _element('methodology') methodology_el.text = methodology child_elements.append(methodology_el) child_elements.sort(key=image_capture_order) for element in child_elements: container.append(element) return container
def capture_information(created=None, producer=None, device=None): """ Returns the MIX GeneralCaptureInformation element. :created: The image datetime created as a string :producer: The image producer as a string :device: The image creation device classification as a string Returns the following sorted ElementTree structure:: <mix:GeneralCaptureInformation> <mix:dateTimeCreated>foo</mix:dateTimeCreated> <mix:imageProducer>foo</mix:imageProducer> <mix:captureDevice>foo</mix:captureDevice> </mix:GeneralCaptureInformation> """ container = _element('GeneralCaptureInformation') if created: created_el = _subelement(container, 'dateTimeCreated') created_el.text = created if producer: producer = _ensure_list(producer) for item in producer: producer_el = _subelement(container, 'imageProducer') producer_el.text = item if device: if device in CAPTURE_DEVICE_TYPES: device_el = _subelement(container, 'captureDevice') device_el.text = device else: raise RestrictedElementError(device, 'captureDevice', CAPTURE_DEVICE_TYPES) return container
def _normalized_byteorder(byte_order): """ Tries to fix the byte_order so that the value corresponds to the values allowed in the MIX schema. Normalizes hyphens, underscores and capitalized letters. Raises an exception if bytOrder couldn't be normalized. :byte_order: The input byte order as a string :returns: The (fixed) byte order as a string """ byte_order = byte_order.replace('-', ' ').replace('_', ' ') byte_order = byte_order.lower() if byte_order in BYTE_ORDER_TYPES: return byte_order if 'big' in byte_order and 'endian' in byte_order: return 'big endian' if 'little' in byte_order and 'endian' in byte_order: return 'little endian' raise RestrictedElementError(byte_order, 'byteOrder', BYTE_ORDER_TYPES)
def fixity(algorithm=None, digest=None, originator=None): """ Returns the MIX Fixity element. :algorithm: The message digest algorithm as a string :digest: The message digest as a string :originator: The message digest creator agent as a string Returns the following ElementTree structure:: <mix:Fixity> <mix:messageDigestAlgorithm>MD5</mix:messageDigestAlgorithm> <mix:messageDigest>foo</mix:messageDigest> <mix:messageDigestOriginator>foo</mix:messageDigestOriginator> </mix:Fixity> """ container = _element('Fixity') if algorithm: if algorithm in DIGEST_ALGORITHMS: algorithm_el = _subelement(container, 'messageDigestAlgorithm') algorithm_el.text = algorithm else: raise RestrictedElementError(algorithm, 'messageDigestAlgorithm', DIGEST_ALGORITHMS) if digest: digest_el = _subelement(container, 'messageDigest') digest_el.text = digest if originator: originator_el = _subelement(container, 'messageDigestOriginator') originator_el.text = originator return container
def max_optical_resolution(x_resolution=None, y_resolution=None, unit=None): """ Returns the MIX MaximumOpticalResolution element. :x_resolution: The x resolution of the scanning sensor as an integer :y_resolution: The y resolution of the scanning sensor as an integer :unit: The unit of the scanning sensor resolution as a string Returns the following sorted ElementTree structure:: <mix:MaximumOpticalResolution> <mix:xOpticalResolution>foo</mix:xOpticalResolution> <mix:yOpticalResolution>foo</mix:yOpticalResolution> <mix:opticalResolutionUnit>foo</mix:opticalResolutionUnit> </mix:MaximumOpticalResolution> """ container = _element('MaximumOpticalResolution') if x_resolution: x_resolution_el = _subelement(container, 'xOpticalResolution') x_resolution_el.text = six.text_type(x_resolution) if y_resolution: y_resolution_el = _subelement(container, 'yOpticalResolution') y_resolution_el.text = six.text_type(y_resolution) if unit: if unit in OPTICAL_RESOLUTION_UNITS: unit_el = _subelement(container, 'opticalResolutionUnit') unit_el.text = unit else: raise RestrictedElementError(unit, 'opticalResolutionUnit', OPTICAL_RESOLUTION_UNITS) return container
def ycbcr(subsample_horiz=None, subsample_vert=None, positioning=None, luma_red=None, luma_green=None, luma_blue=None): """ Returns the MIX YCbCr element and its subelements. :subsample_horiz: The horizontal subsample factor as a string :subsample_vert: The vertical subsample factor as a string :positioning: The positions of subsamples as a string :luma_red: The red luminance value as a list (or integer) :luma_green: The green luminane value as a list (or integer) :luma_blue: The blue luminance value as a list (or integer) Returns the following sorted ElementTree structure:: <mix:YCbCr> <mix:YCbCrSubSampling> <mix:yCbCrSubsampleHoriz>1</mix:yCbCrSubsampleHoriz> <mix:yCbCrSubsampleVert>2</mix:yCbCrSubsampleVert> </mix:YCbCrSubSampling/> <mix:yCbCrPositioning>1</mix:yCbCrPositioning> <mix:YCbCrCoefficients> <mix:lumaRed> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:lumaRed> <mix:lumaGreen> <mix:numerator>20</mix:numerator> <mix:denominator>1</mix:denominator> </mix:lumaGreen> <mix:lumaBlue> <mix:numerator>30</mix:numerator> <mix:denominator>1</mix:denominator> </mix:lumaBlue> </mix:YCbCrCoefficients/> </mix:YCbCr> """ container = _element('YCbCr') if subsample_horiz or subsample_vert: subsample_container = _subelement(container, 'YCbCrSubSampling') if subsample_horiz: if subsample_horiz in YCBCR_SUBSAMPLE_TYPES: subsample_horiz_el = _subelement(subsample_container, 'yCbCrSubsampleHoriz') subsample_horiz_el.text = subsample_horiz else: raise RestrictedElementError(subsample_horiz, 'yCbCrSubsampleHoriz', YCBCR_SUBSAMPLE_TYPES) if subsample_vert: if subsample_vert in YCBCR_SUBSAMPLE_TYPES: subsample_vert_el = _subelement(subsample_container, 'yCbCrSubsampleVert') subsample_vert_el.text = subsample_vert else: raise RestrictedElementError(subsample_vert, 'yCbCrSubsampleVert', YCBCR_SUBSAMPLE_TYPES) if positioning: if positioning in YCBCR_POSITIONING_TYPES: positioning_el = _subelement(container, 'yCbCrPositioning') positioning_el.text = positioning else: raise RestrictedElementError(positioning, 'yCbCrPositioning', YCBCR_POSITIONING_TYPES) if luma_red or luma_green or luma_blue: luma_container = _subelement(container, 'YCbCrCoefficients') if luma_red: _rationaltype_element('lumaRed', luma_red, parent=luma_container) if luma_green: _rationaltype_element('lumaGreen', luma_green, parent=luma_container) if luma_blue: _rationaltype_element('lumaBlue', luma_blue, parent=luma_container) return container
def source_size(x_value=None, x_unit=None, y_value=None, y_unit=None, z_value=None, z_unit=None): """ Returns the MIX SourceSize element. :x_value: The source X value (width) as an integer :x_unit: The unit of the source X value (width) as a string :y_value: The source Y value (height) as an integer :y_unit: The unit of the source Y value (height) as a string :z_value: The source Z value (depth) as an integer :z_unit: The unit of the source z value (depth) as a string Returns the following sorted ElementTree structure:: <mix:SourceSize> <mix:SourceXDimension> <mix:sourceXDimensionValue>1.23</mix:sourceXDimensionValue> <mix:sourceXDimensionUnit>mm.</mix:sourceXDimensionUnit> </mix:SourceXDimension> <mix:SourceYDimension> <mix:sourceYDimensionValue>1.23</mix:sourceXDimensionValue> <mix:sourceYDimensionUnit>mm.</mix:sourceXDimensionUnit> </mix:SourceYDimension> <mix:SourceZDimension> <mix:sourceZDimensionValue>1.23</mix:sourceXDimensionValue> <mix:sourceZDimensionUnit>mm.</mix:sourceXDimensionUnit> </mix:SourceZDimension> </mix:SourceSize> """ container = _element('SourceSize') if x_value or x_unit: x_dimension = _subelement(container, 'SourceXDimension') if x_value: x_value_el = _subelement(x_dimension, 'sourceXDimensionValue') x_value_el.text = x_value if x_unit: if x_unit in DIMENSION_UNITS: x_unit_el = _subelement(x_dimension, 'sourceXDimensionUnit') x_unit_el.text = x_unit else: raise RestrictedElementError(x_unit, 'sourceXDimensionUnit', DIMENSION_UNITS) if y_value or y_unit: y_dimension = _subelement(container, 'SourceYDimension') if y_value: y_value_el = _subelement(y_dimension, 'sourceYDimensionValue') y_value_el.text = y_value if y_unit: if y_unit in DIMENSION_UNITS: y_unit_el = _subelement(y_dimension, 'sourceYDimensionUnit') y_unit_el.text = y_unit else: raise RestrictedElementError(y_unit, 'sourceYDimensionUnit', DIMENSION_UNITS) if z_value or z_unit: z_dimension = _subelement(container, 'SourceZDimension') if z_value: z_value_el = _subelement(z_dimension, 'sourceZDimensionValue') z_value_el.text = z_value if z_unit: if z_unit in DIMENSION_UNITS: z_unit_el = _subelement(z_dimension, 'sourceZDimensionUnit') z_unit_el.text = z_unit else: raise RestrictedElementError(z_unit, 'sourceZDimensionUnit', DIMENSION_UNITS) return container