def test_digitalobjectinformation(): """Test that the element BasicDigitalObjectInformation is created correctly and that the elements are sorted as intended. """ compr = compression(compression_scheme='jpeg') format_des = format_designation(format_name='jpeg', format_version='1.01') ident = _element('ObjectIdentifier') format_reg = _element('FormatRegistry') fix = _element('Fixity') mix = digital_object_information( byte_order='big endian', file_size=1234, child_elements=[compr, format_des, fix, format_reg, ident]) xml_str = ('<mix:BasicDigitalObjectInformation xmlns:mix=' '"http://www.loc.gov/mix/v20"><mix:ObjectIdentifier/>' '<mix:fileSize>1234</mix:fileSize><mix:FormatDesignation>' '<mix:formatName>jpeg</mix:formatName><mix:formatVersion>1.01' '</mix:formatVersion></mix:FormatDesignation>' '<mix:FormatRegistry/><mix:byteOrder>big endian</mix:byteOrder>' '<mix:Compression><mix:compressionScheme>jpeg' '</mix:compressionScheme></mix:Compression><mix:Fixity/>' '</mix:BasicDigitalObjectInformation>') assert h.compare_trees(mix, ET.fromstring(xml_str)) assert mix.xpath('./*')[1].tag == '{http://www.loc.gov/mix/v20}fileSize' assert mix.xpath('./*')[2].tag == \ '{http://www.loc.gov/mix/v20}FormatDesignation' assert mix.xpath('./*')[4].tag == '{http://www.loc.gov/mix/v20}byteOrder' assert mix.xpath('./*')[5].tag == '{http://www.loc.gov/mix/v20}Compression'
def test_mix(): """ Tests that the mix root element is created and tests that the child elements in the mix root are sorted properly. """ mix1 = mix() assert mix1.xpath('.')[0].tag == '{http://www.loc.gov/mix/v20}mix' assert len(mix1) == 0 child_elems = [] information = _element('BasicDigitalObjectInformation') child_elems.append(information) history = _element('ChangeHistory') child_elems.append(history) capture = _element('ImageCaptureMetadata') child_elems.append(capture) mix2 = mix(child_elements=child_elems) assert len(mix2) == 3 assert mix2.xpath('./*')[0].tag == \ '{http://www.loc.gov/mix/v20}BasicDigitalObjectInformation' assert mix2.xpath('./*')[1].tag == \ '{http://www.loc.gov/mix/v20}ImageCaptureMetadata' assert mix2.xpath('./*')[2].tag == \ '{http://www.loc.gov/mix/v20}ChangeHistory'
def source_information(source_type=None, child_elements=None): """ Returns the MIX SourceInformation element. :source_type: The source type as a string :child_elements: Child elements as a list Returns the following sorted ElementTree structure:: <mix:SourceInformation> <mix:sourceType>foo</mix:sourceType> <mix:SourceID/> <mix:SourceSize/> </mix:SourceInformation> """ container = _element('SourceInformation') if child_elements is None: child_elements = [] if source_type: source_type_el = _element('sourceType') source_type_el.text = source_type child_elements.append(source_type_el) child_elements.sort(key=source_information_order) for element in child_elements: container.append(element) return container
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 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 test_source_information(): """Tests that the element SourceInformation is created correctly.""" s_id = _element('SourceID') size = _element('SourceSize') mix = source_information(source_type='test', child_elements=[size, s_id]) xml_str = ('<mix:SourceInformation xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:sourceType>test</mix:sourceType><mix:SourceID/>' '<mix:SourceSize/></mix:SourceInformation>') assert h.compare_trees(mix, ET.fromstring(xml_str))
def test_ref_black_white(): """Test that the element ReferenceBlackWhite is created correctly.""" comp1 = _element('Component') comp2 = _element('Component') mix = ref_black_white(child_elements=[comp1, comp2]) xml_str = ('<mix:ReferenceBlackWhite ' 'xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:Component/><mix:Component/>' '</mix:ReferenceBlackWhite>') assert h.compare_trees(mix, ET.fromstring(xml_str))
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 test_element(): """ Tests the _element function by asserting that the element is created correctly both with and without a prefix. Also tests that the correct namespace is used in the element and that the namespace is mapped to the intended prefix when serializing the XML to string. """ elem1 = _element('test') assert elem1.tag == '{http://www.loc.gov/mix/v20}test' assert ET.tostring(elem1) == ET.tostring(ET.fromstring( '<mix:test xmlns:mix="http://www.loc.gov/mix/v20"/>')) elem2 = _element('test', 'pre') assert elem2.tag == '{http://www.loc.gov/mix/v20}preTest'
def test_change_history(): """Test that the element ChangeHistory is created correctly and that the elements are sorted as intended. """ processing = _element('ImageProcessing') prev_metadata = _element('PreviousImageMetadata') mix = change_history(child_elements=[prev_metadata, processing]) xml_str = ('<mix:ChangeHistory xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:ImageProcessing/><mix:PreviousImageMetadata/>' '</mix:ChangeHistory>') assert h.compare_trees(mix, ET.fromstring(xml_str))
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 scanning_software(name=None, version=None): """ Returns the MIX ScanningSystemSoftware element. :name: The scanning software name as a string :version: The scanning software version as a string Returns the following sorted ElementTree structure:: <mix:ScanningSystemSoftware> <mix:scanningSoftwareName>foo</mix:scanningSoftwareName> <mix:scanningSoftwareVersionNo>foo</mix:scanningSoftwareVersionNo> </mix:ScanningSystemSoftware> """ container = _element('ScanningSystemSoftware') if name: name_el = _subelement(container, 'scanningSoftwareName') name_el.text = name if version: version_el = _subelement(container, 'scanningSoftwareVersionNo') version_el.text = version return container
def color_map(reference=None, embedded=None): """ Returns the MIX Colormap element. :reference: The location of the referenced color map as a string :embedded: The embedded color map as base64-encoded data Returns the following ElementTree structure:: <mix:Colormap> <mix:colormapReference>http://foo</mix:colormapReference> <mix:embeddedColormap>foo</mix:embeddedColormap> </mix:Colormap> """ container = _element('Colormap') if reference: reference_el = _subelement(container, 'colormapReference') reference_el.text = reference if embedded: embedded_el = _subelement(container, 'embeddedColormap') embedded_el.text = six.text_type(embedded) 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 photometric_interpretation(color_space=None, child_elements=None): """" Returns the MIX PhotometricInterpretation element. :color_space: The color space as a string :child_elements: Child elements as a list Returns the following sorted ElementTree structure:: <mix:PhotometricInterpretation> <mix:colorSpace>RGB</mix:colorSpace> <mix:ColorProfile/> <mix:YCbCr/> <mix:ReferenceBlackWhite/> </mix:PhotometricInterpretation> """ container = _element('PhotometricInterpretation') if color_space: color_space_el = _subelement(container, 'colorSpace') color_space_el.text = color_space if child_elements: child_elements.sort(key=photom_interpret_order) for element in child_elements: container.append(element) return container
def image_characteristics(width=None, height=None, child_elements=None): """ Returns the MIX BasicImageCharacteristics element. :width: The image width in pixels as an integer :height: The image height in pixels as an integer :child_elements: Child elements as a list Returns the following sorted ElementTree structure:: <mix:BasicImageCharacteristics> <mix:imageWidth>20</mix:imageWidth> <mix:imageHeight>10</mix:imageHeight> <mix:PhotometricInterpretation/> </mix:BasicImageCharacteristics> """ container = _element('BasicImageCharacteristics') if width: width_el = _subelement(container, 'imageWidth') width_el.text = six.text_type(width) if height: height_el = _subelement(container, 'imageHeight') height_el.text = six.text_type(height) if child_elements: for element in child_elements: container.append(element) return container
def source_id(source_idtype=None, source_idvalue=None): """ Returns the MIX SourceID element. :source_idtype: The source ID type as a string :source_idvalue: The source ID value as a string Returns the following sorted ElementTree structure:: <mix:SourceID> <mix:sourceIDType>local</mix:sourceIDType> <mix:sourceIDValue>foo</mix:sourceIDValue> </mix:SourceID> """ container = _element('SourceID') if source_idtype: source_idtype_el = _subelement(container, 'sourceIDType') source_idtype_el.text = source_idtype if source_idvalue: source_idvalue_el = _subelement(container, 'sourceIDValue') source_idvalue_el.text = source_idvalue return container
def image_information(child_elements=None): """ Returns the MIX BasicImageInformation element. The subelements are sorted according to the order as noted in the schema. :child_elements: Child elements as a list Returns the following sorted ElementTree structure:: <mix:BasicImageInformation> <mix:BasicImageCharacteristics/> <mix:SpecialFormatCharacteristics/> </mix:BasicImageInformation> """ container = _element('BasicImageInformation') if child_elements: child_elements.sort(key=image_information_order) for element in child_elements: container.append(element) 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 identifier(id_type=None, id_value=None): """ Returns the MIX ObjectIdentifier element. :id_type: The identifier type as a string :id_value: The identifier value as a string Returns the following ElementTree structure:: <mix:ObjectIdentifier> <mix:objectIdentifierType>local</mix:objectIdentifierType> <mix:objectIdentifierValue>foo</mix:objectIdentifierValue> </mix:ObjectIdentifier> """ container = _element('ObjectIdentifier') if id_type: id_type_el = _subelement(container, 'objectIdentifierType') id_type_el.text = id_type if id_value: id_value_el = _subelement(container, 'objectIdentifierValue') id_value_el.text = id_value return container
def test_camera_capture_settings(): """ Tests that the element CameraCaptureSettings is created correctly and that the subelements are sorted properly. """ gps = _element('GPSData') img = _element('ImageData') mix = camera_capture_settings(child_elements=[gps, img]) xml_str = ('<mix:CameraCaptureSettings ' 'xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:ImageData/><mix:GPSData/>' '</mix:CameraCaptureSettings>') assert h.compare_trees(mix, ET.fromstring(xml_str))
def format_registry(registry_name=None, registry_key=None): """ Returns the MIX FormatRegistry element. :registry_name: The file format registry name as a string :registry_key: The file format registry key as a string Returns the following ElementTree structure:: <mix:FormatRegistry> <mix:formatRegistryName>pronom</mix:formatRegistryName> <mix:formatRegistryKey>fmt/43</mix:formatRegistryKey> </mix:FormatRegistry> """ container = _element('FormatRegistry') if registry_name: registry_name_el = _subelement(container, 'formatRegistryName') registry_name_el.text = registry_name if registry_key: registry_key_el = _subelement(container, 'formatRegistryKey') registry_key_el.text = registry_key return container
def format_designation(format_name=None, format_version=None): """ Returns the MIX FormatDesignation element. :format_name: The file format name as a string :format_version: The file format version as a string Returns the following ElementTree structure:: <mix:FormatDesignation> <mix:formatName>image/jpeg</mix:formatName> <mix:formatVersion>1.01</mix:formatVersion> </mix:FormatDesignation> """ container = _element('FormatDesignation') if format_name: format_name_el = _subelement(container, 'formatName') format_name_el.text = format_name if format_version: format_version_el = _subelement(container, 'formatVersion') format_version_el.text = format_version return container
def white_point(x_value=None, y_value=None): """ Returns the MIX WhitePoint element. :x_value: The X value of white point chromaticity as a list :y_value: The Y value of white point chromaticity as a list Returns the following ElementTree structure:: <mix:WhitePoint> <mix:whitePointXValue> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:whitePointXValue> <mix:whitePointYValue> <mix:numerator>10</mix:numerator> <mix:denominator>1</mix:denominator> </mix:whitePointYValue> </mix:WhitePoint> """ container = _element('WhitePoint') if x_value: _rationaltype_element('whitePointXValue', x_value, parent=container) if y_value: _rationaltype_element('whitePointYValue', y_value, parent=container) return container
def test_assessment_metadata(): """ Test that the element ImageAssessmentMetadata is created correctly and that the subelements are properly sorted. """ target = _element('TargetData') spatial = _element('SpatialMetrics') encoding = _element('ImageColorEncoding') mix = image_assessment_metadata(child_elements=[target, spatial, encoding]) xml_str = ('<mix:ImageAssessmentMetadata xmlns:mix=' '"http://www.loc.gov/mix/v20">' '<mix:SpatialMetrics/><mix:ImageColorEncoding/>' '<mix:TargetData/></mix:ImageAssessmentMetadata>') assert h.compare_trees(mix, ET.fromstring(xml_str))
def digital_object_information(byte_order=None, file_size=None, child_elements=None): """ Returns the MIX BasicDigitalObjectInformation element. The subelements are sorted according to the order as noted in the schema. :byte_order: The byte order as a string :file_size: The file size in bytes as an integer :child_elements: Child elements as a list Returns the following sorted ElementTree structure:: <mix:BasicDigitalObjectInformation> <mix:ObjectIdentifier/> <mix:fileSize>1234</mix:fileSize> <mix:FormatDesignation/> <mix:FormatRegistry/> <mix:byteOrder>big endian</mix:byteOrder> <mix:Compression/> <mix:Fixity/> </mix:BasicDigitalObjectInformation> """ if child_elements is None: child_elements = [] container = _element('BasicDigitalObjectInformation') if file_size: file_size_el = _element('fileSize') file_size_el.text = six.text_type(file_size) child_elements.append(file_size_el) if byte_order: byte_order_el = _element('byteOrder') byte_order_el.text = _normalized_byteorder(byte_order) child_elements.append(byte_order_el) child_elements.sort(key=basic_do_order) for element in child_elements: container.append(element) return container
def test_image_information(): """ Tests that the element BasicImageInformation is created correctly and that the subelements are sorted properly. """ img_characteristics = _element('BasicImageCharacteristics') f_characteristics = _element('SpecialFormatCharacteristics') mix = image_information( child_elements=[f_characteristics, img_characteristics]) xml_str = ('<mix:BasicImageInformation xmlns:mix=' '"http://www.loc.gov/mix/v20">' '<mix:BasicImageCharacteristics/>' '<mix:SpecialFormatCharacteristics/>' '</mix:BasicImageInformation>') assert h.compare_trees(mix, ET.fromstring(xml_str))
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 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 test_photometric_interpretation(): """ Test that the element PhotometricInterpretation is created correctly. """ profile = _element('ColorProfile') ycc = _element('YCbCr') ref_bw = _element('ReferenceBlackWhite') mix = photometric_interpretation(color_space='foo', child_elements=[ycc, ref_bw, profile]) xml_str = ('<mix:PhotometricInterpretation ' 'xmlns:mix="http://www.loc.gov/mix/v20">' '<mix:colorSpace>foo</mix:colorSpace>' '<mix:ColorProfile/><mix:YCbCr/><mix:ReferenceBlackWhite/>' '</mix:PhotometricInterpretation>') assert h.compare_trees(mix, ET.fromstring(xml_str))