def test22_write_extra_info(self): """Test writing of extraQualities, extraFormats, extraFeatures.""" i = IIIFInfo(api_version='3.0', id="https://example.org/svc/a", width=1, height=2, extra_formats=['fmt1', 'fmt2']) j = json.loads(i.as_json()) self.assertEqual(j['extraFormats'], ['fmt1', 'fmt2']) self.assertNotIn('extraQualities', j) self.assertNotIn('extraFeatures', j) # i = IIIFInfo(api_version='3.0', id="https://example.org/svc/a", width=1, height=2, extra_qualities=['aaa1', 'aaa2']) j = json.loads(i.as_json()) self.assertNotIn('extraFormats', j) self.assertEqual(j['extraQualities'], ['aaa1', 'aaa2']) self.assertNotIn('extraFeatures', j) # i = IIIFInfo(api_version='3.0', id="https://example.org/svc/a", width=1, height=2, extra_features=['feat1', 'http://example.org/feat2']) j = json.loads(i.as_json()) self.assertNotIn('extraFormats', j) self.assertNotIn('extraQualities', j) self.assertEqual(j['extraFeatures'], ['feat1', 'http://example.org/feat2'])
def test01_minmal(self): # Just do the trivial JSON test ir = IIIFInfo(server_and_prefix="http://example.com",identifier="i1",api_version='1.1') self.assertEqual( ir.as_json(validate=False), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1", \n "width": 100\n}' )
def test01_minmal(self): # Just do the trivial JSON test ir = IIIFInfo(identifier="i1",api_version='1.0') self.assertEqual( ir.as_json(validate=False), '{\n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "height": 200, \n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1", \n "width": 100\n}' )
def test01_minmal(self): # Just do the trivial JSON test # ?? should this empty case raise and error instead? ir = IIIFInfo(identifier="http://example.com/i1", api_version='2.1') self.assertEqual( ir.as_json(validate=False), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image", \n "width": 100\n}' )
def test01_minmal(self): # Just do the trivial JSON test # ?? should this empty case raise and error instead? ir = IIIFInfo(identifier="http://example.com/i1") self.assertEqual( ir.as_json(validate=False), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image"\n}' ) ir.width=100 ir.height=200 self.assertEqual( ir.as_json(), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image", \n "width": 100\n}' )
def test01_minmal(self): """Trivial JSON test.""" ir = IIIFInfo(identifier="i1", api_version='1.0') self.assertJSONEqual(ir.as_json( validate=False), '{\n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1"\n}') ir.width = 100 ir.height = 200 self.assertJSONEqual(ir.as_json( ), '{\n "height": 200, \n "identifier": "i1", \n "profile": "http://library.stanford.edu/iiif/image-api/compliance.html#level1", \n "width": 100\n}')
def test01_minmal(self): """Trivial JSON test.""" ir = IIIFInfo(server_and_prefix="http://example.com", identifier="i1", api_version='1.1') self.assertJSONEqual(ir.as_json( validate=False), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1"\n}') ir.width = 100 ir.height = 200 self.assertJSONEqual(ir.as_json( ), '{\n "@context": "http://library.stanford.edu/iiif/image-api/1.1/context.json", \n "@id": "http://example.com/i1", \n "height": 200, \n "profile": "http://library.stanford.edu/iiif/image-api/1.1/compliance.html#level1", \n "width": 100\n}')
def test21_write_profile(self): """Test writing of profile information.""" i = IIIFInfo(api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], formats=["fmt1", "fmt2"]) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1], {'formats': ['fmt1', 'fmt2']}) i = IIIFInfo(api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], qualities=None) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 1) self.assertEqual(j['profile'][0], 'pfl') i = IIIFInfo(api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], qualities=['q1', 'q2', 'q0']) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1], {'qualities': ['q1', 'q2', 'q0']}) i = IIIFInfo(api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], supports=['a', 'b']) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1], {'supports': ['a', 'b']}) i = IIIFInfo(api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], formats=["fmt1", "fmt2"], qualities=['q1', 'q2', 'q0'], supports=['a', 'b']) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1]['formats'], ['fmt1', 'fmt2']) self.assertEqual(j['profile'][1]['qualities'], ['q1', 'q2', 'q0']) self.assertEqual(j['profile'][1]['supports'], ['a', 'b'])
def generate(self, src=None, identifier=None): """Generate static files for one source image.""" self.src=src self.identifier=identifier # Get image details and calculate tiles im=IIIFManipulatorPIL() im.srcfile = self.src im.do_first() width = im.width height = im.height #print "w=%d h=%d ts=%d" % (im.width,im.height,tilesize) xtiles = int(width/self.tilesize) ytiles = int(height/self.tilesize) max_tiles = xtiles if (xtiles>ytiles) else ytiles scale_factors = [ 1 ] factor = 1 for n in range(10): if (factor >= max_tiles): break factor = factor+factor scale_factors.append(factor) # Setup destination and IIIF identifier self.setup_destination() # Write out images for (region,size) in static_partial_tile_sizes(width,height,self.tilesize,scale_factors): self.generate_tile(region,size) sizes = [] for (size) in static_full_sizes(width,height,self.tilesize): #FIXME - see https://github.com/zimeon/iiif/issues/9 #sizes.append({'width': size[0], 'height': size[1]}) self.generate_tile('full',size) # Write info.json qualities = ['default'] if (self.api_version>'1.1') else ['native'] info=IIIFInfo(level=0, server_and_prefix=self.prefix, identifier=self.identifier, width=width, height=height, scale_factors=scale_factors, tile_width=self.tilesize, tile_height=self.tilesize, formats=['jpg'], qualities=qualities, sizes=sizes, api_version=self.api_version) json_file = os.path.join(self.dst,self.identifier,'info.json') if (self.dryrun): print "dryrun mode, would write the following files:" print "%s / %s/%s" % (self.dst, self.identifier, 'info.json') self.logger.info(info.as_json()) else: with open(json_file,'w') as f: f.write(info.as_json()) f.close() self.logger.info("Written %s"%(json_file)) print
def test20_write_example_in_spec(self): """Test creation of example from spec.""" i = IIIFInfo( id="http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C", # "protocol" : "http://iiif.io/api/image", width=6000, height=4000, sizes=[ {"width": 150, "height": 100}, {"width": 600, "height": 400}, {"width": 3000, "height": 2000} ], tiles=[ {"width": 512, "scaleFactors": [1, 2, 4, 8, 16]} ], profile=["http://iiif.io/api/image/2/level2.json"], formats=["gif", "pdf"], qualities=["color", "gray"], supports=["canonicalLinkHeader", "rotationArbitrary", "profileLinkHeader", "http://example.com/feature/"], service={ "@context": "http://iiif.io/api/annex/service/physdim/1/context.json", "profile": "http://iiif.io/api/annex/service/physdim", "physicalScale": 0.0025, "physicalUnits": "in" } ) reparsed_json = json.loads(i.as_json()) example_json = json.load( open('tests/testdata/info_json_2_0/info_from_spec.json')) self.maxDiff = 4000 self.assertEqual(reparsed_json, example_json)
def do_GET_body(self): """Create body of GET.""" iiif = self.iiif if (len(self.path) > 1024): raise IIIFError(code=414, text="URI Too Long: Max 1024 chars, got %d\n" % len(self.path)) try: # self.path has leading / then identifier/params... self.path = self.path.lstrip('/') sys.stderr.write("path = %s" % (self.path)) iiif.parse_url(self.path) except Exception as e: # Something completely unexpected => 500 raise IIIFError( code=500, text= "Internal Server Error: unexpected exception parsing request (" + str(e) + ")") # Now we have a full iiif request if (re.match('[\w\.\-]+$', iiif.identifier)): file = os.path.join(TESTIMAGE_DIR, iiif.identifier) if (not os.path.isfile(file)): images_available = "" for image_file in os.listdir(TESTIMAGE_DIR): if (os.path.isfile(os.path.join(TESTIMAGE_DIR, image_file))): images_available += " " + image_file + "\n" raise IIIFError(code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Local image files available:\n" + images_available) else: raise IIIFError( code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Only local test images and http: URIs for images are supported.\n" ) # Now know image is OK manipulator = IIIFRequestHandler.manipulator_class() # Stash manipulator object so we can cleanup after reading file self.manipulator = manipulator self.compliance_uri = manipulator.compliance_uri if (iiif.info): # get size manipulator.srcfile = file manipulator.do_first() # most of info.json comes from config, a few things # specific to image i = IIIFInfo() i.identifier = self.iiif.identifier i.width = manipulator.width i.height = manipulator.height import io return (io.StringIO(i.as_json()), "application/json") else: (outfile, mime_type) = manipulator.derive(file, iiif) return (open(outfile, 'r'), mime_type)
def image_information_response(self): """Parse image information request and create response.""" dr = degraded_request(self.identifier) if (dr): self.logger.info("image_information: degraded %s -> %s" % (self.identifier,dr)) self.degraded = self.identifier self.identifier = dr else: self.logger.info("image_information: %s" % (self.identifier)) # get size self.manipulator.srcfile=self.file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image info = { 'tile_height': self.config.tile_height, 'tile_width': self.config.tile_width, 'scale_factors' : self.config.scale_factors } i = IIIFInfo(conf=info,api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height if (self.api_version>='2.0'): i.qualities = [ "default", "color", "gray" ] #FIXME - should come from manipulator else: i.qualities = [ "native", "color", "gray" ] #FIXME - should come from manipulator i.formats = [ "jpg", "png" ] #FIXME - should come from manipulator if (self.auth): self.auth.add_services(i) return self.make_response(i.as_json(),content_type=self.json_mime_type)
def test20_write_example_in_spec(self): i = IIIFInfo( api_version='2.1', id="http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C", #"protocol" : "http://iiif.io/api/image", width=6000, height=4000, sizes=[ {"width" : 150, "height" : 100}, {"width" : 600, "height" : 400}, {"width" : 3000, "height": 2000} ], tiles=[ {"width" : 512, "scaleFactors" : [1,2,4,8,16]} ], profile="http://iiif.io/api/image/2/level2.json", formats = [ "gif", "pdf" ], qualities = [ "color", "gray" ], supports = [ "canonicalLinkHeader", "rotationArbitrary", "profileLinkHeader", "http://example.com/feature/" ], service={ "@context": "http://iiif.io/api/annex/service/physdim/1/context.json", "profile": "http://iiif.io/api/annex/service/physdim", "physicalScale": 0.0025, "physicalUnits": "in" } ) reparsed_json = json.loads( i.as_json() ) example_json = json.load( open('test_info/2.1/info_from_spec.json') ) self.maxDiff = 4000 self.assertEqual( reparsed_json, example_json )
def do_GET_body(self): """Create body of GET.""" iiif = self.iiif if len(self.path) > 1024: raise IIIFError(code=414, text="URI Too Long: Max 1024 chars, got %d\n" % len(self.path)) try: # self.path has leading / then identifier/params... self.path = self.path.lstrip("/") sys.stderr.write("path = %s" % (self.path)) iiif.parse_url(self.path) except Exception as e: # Something completely unexpected => 500 raise IIIFError( code=500, text="Internal Server Error: unexpected exception parsing request (" + str(e) + ")" ) # Now we have a full iiif request if re.match("[\w\.\-]+$", iiif.identifier): file = os.path.join(TESTIMAGE_DIR, iiif.identifier) if not os.path.isfile(file): images_available = "" for image_file in os.listdir(TESTIMAGE_DIR): if os.path.isfile(os.path.join(TESTIMAGE_DIR, image_file)): images_available += " " + image_file + "\n" raise IIIFError( code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Local image files available:\n" + images_available, ) else: raise IIIFError( code=404, parameter="identifier", text="Image resource '" + iiif.identifier + "' not found. Only local test images and http: URIs for images are supported.\n", ) # Now know image is OK manipulator = IIIFRequestHandler.manipulator_class() # Stash manipulator object so we can cleanup after reading file self.manipulator = manipulator self.compliance_uri = manipulator.compliance_uri if iiif.info: # get size manipulator.srcfile = file manipulator.do_first() # most of info.json comes from config, a few things # specific to image i = IIIFInfo() i.identifier = self.iiif.identifier i.width = manipulator.width i.height = manipulator.height import io return (io.StringIO(i.as_json()), "application/json") else: (outfile, mime_type) = manipulator.derive(file, iiif) return (open(outfile, "r"), mime_type)
def test02_scale_factor(self): ir = IIIFInfo(width=1, height=2, scale_factors=[1, 2], api_version='1.1') #self.assertRegexpMatches( ir.as_json(validate=False), r'"scale_factors": \[\s*1' ) #,\s*2\s*]' ) #no assertRegexpMatches in 2.6 json = ir.as_json(validate=False) self.assertTrue(re.search(r'"scale_factors": \[\s*1', json))
def test01_empty_auth_defined(self): """Test empty auth.""" info = IIIFInfo(identifier="http://example.com/i1", api_version='2.1') auth = IIIFAuth() auth.add_services(info) self.assertJSONEqual(info.as_json( validate=False), '{\n "@context": "http://iiif.io/api/image/2/context.json", \n "@id": "http://example.com/i1", \n "profile": [\n "http://iiif.io/api/image/2/level1.json"\n ], \n "protocol": "http://iiif.io/api/image"\n}') self.assertEqual(info.service, None)
def test02_scale_factor(self): """Test scale factor.""" ir = IIIFInfo(width=1, height=2, scale_factors=[1, 2], api_version='1.0') json = ir.as_json(validate=False) self.assertRegexpMatches(json, r'"scale_factors": \[\s*1')
def test02_scale_factor(self): """Test scale factor.""" ir = IIIFInfo(width=1, height=2, scale_factors=[ 1, 2], api_version='1.1') # self.assertRegexpMatches( ir.as_json(validate=False), # r'"scale_factors": \[\s*1' ) #,\s*2\s*]' ) #no assertRegexpMatches in # 2.6 json = ir.as_json(validate=False) self.assertTrue(re.search(r'"scale_factors": \[\s*1', json))
def test21_write_profile(self): """Test writing of profile information.""" i = IIIFInfo(api_version='3.0', id="https://example.org/svc/a", width=1, height=2, profile='pfl') j = json.loads(i.as_json()) self.assertEqual(j['profile'], 'pfl')
def test01_minmal(self): """Trivial JSON test.""" # ?? should this empty case raise and error instead? ir = IIIFInfo(identifier="https://example.com/i1", api_version='3.0') self.assertJSONEqual( ir.as_json(validate=False), '{\n "@context": "http://iiif.io/api/image/3/context.json", \n ' '"id": "https://example.com/i1", \n "profile": "level1", \n ' '"protocol": "http://iiif.io/api/image", "type": "ImageService3"\n}' ) ir.width = 100 ir.height = 200 self.assertJSONEqual( ir.as_json(), '{\n "@context": "http://iiif.io/api/image/3/context.json", \n ' '"height": 200, \n "id": "https://example.com/i1", \n ' '"profile": "level1", \n "protocol": "http://iiif.io/api/image", \n' '"type": "ImageService3",\n "width": 100\n}')
def test21_write_profile(self): """Test writing of profile information.""" i = IIIFInfo( api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], formats=["fmt1", "fmt2"]) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1], {'formats': ['fmt1', 'fmt2']}) i = IIIFInfo( api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], qualities=None) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 1) self.assertEqual(j['profile'][0], 'pfl') i = IIIFInfo( api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], qualities=['q1', 'q2', 'q0']) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1], {'qualities': ['q1', 'q2', 'q0']}) i = IIIFInfo( api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], supports=['a', 'b']) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1], {'supports': ['a', 'b']}) i = IIIFInfo( api_version='2.1', id="http://example.org/svc/a", width=1, height=2, profile=['pfl'], formats=["fmt1", "fmt2"], qualities=['q1', 'q2', 'q0'], supports=['a', 'b']) j = json.loads(i.as_json()) self.assertEqual(len(j['profile']), 2) self.assertEqual(j['profile'][0], 'pfl') self.assertEqual(j['profile'][1]['formats'], ['fmt1', 'fmt2']) self.assertEqual(j['profile'][1]['qualities'], ['q1', 'q2', 'q0']) self.assertEqual(j['profile'][1]['supports'], ['a', 'b'])
def test21_write_profile(self): """Test writing of profile information.""" i = IIIFInfo(id="http://example.org/svc/a", width=1, height=2, profile=["pfl"], formats=["fmt1", "fmt2"]) j = json.loads(i.as_json()) self.assertEqual(len(j["profile"]), 2) self.assertEqual(j["profile"][0], "pfl") self.assertEqual(j["profile"][1], {"formats": ["fmt1", "fmt2"]}) i = IIIFInfo(id="http://example.org/svc/a", width=1, height=2, profile=["pfl"], qualities=None) j = json.loads(i.as_json()) self.assertEqual(len(j["profile"]), 1) self.assertEqual(j["profile"][0], "pfl") i = IIIFInfo(id="http://example.org/svc/a", width=1, height=2, profile=["pfl"], qualities=["q1", "q2", "q0"]) j = json.loads(i.as_json()) self.assertEqual(len(j["profile"]), 2) self.assertEqual(j["profile"][0], "pfl") self.assertEqual(j["profile"][1], {"qualities": ["q1", "q2", "q0"]}) i = IIIFInfo(id="http://example.org/svc/a", width=1, height=2, profile=["pfl"], supports=["a", "b"]) j = json.loads(i.as_json()) self.assertEqual(len(j["profile"]), 2) self.assertEqual(j["profile"][0], "pfl") self.assertEqual(j["profile"][1], {"supports": ["a", "b"]}) i = IIIFInfo( id="http://example.org/svc/a", width=1, height=2, profile=["pfl"], formats=["fmt1", "fmt2"], qualities=["q1", "q2", "q0"], supports=["a", "b"], ) j = json.loads(i.as_json()) self.assertEqual(len(j["profile"]), 2) self.assertEqual(j["profile"][0], "pfl") self.assertEqual(j["profile"][1]["formats"], ["fmt1", "fmt2"]) self.assertEqual(j["profile"][1]["qualities"], ["q1", "q2", "q0"]) self.assertEqual(j["profile"][1]["supports"], ["a", "b"])
def test20_write_example_in_spec(self): """Create example info.json in spec.""" i = IIIFInfo( api_version='2.1', id="http://www.example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C", # "protocol" : "http://iiif.io/api/image", width=6000, height=4000, sizes=[ {"width": 150, "height": 100}, {"width": 600, "height": 400}, {"width": 3000, "height": 2000}], tiles=[ {"width": 512, "scaleFactors": [1, 2, 4]}, {"width": 1024, "height": 2048, "scaleFactors": [8, 16]}], attribution=[ {"@value": "<span>Provided by Example Organization</span>", "@language": "en"}, {"@value": "<span>Darparwyd gan Enghraifft Sefydliad</span>", "@language": "cy"}], logo={"@id": "http://example.org/image-service/logo/full/200,/0/default.png", "service": {"@context": "http://iiif.io/api/image/2/context.json", "@id": "http://example.org/image-service/logo", "profile": "http://iiif.io/api/image/2/level2.json"}}, license=[ "http://example.org/rights/license1.html", "https://creativecommons.org/licenses/by/4.0/"], profile=["http://iiif.io/api/image/2/level2.json"], formats=["gif", "pdf"], qualities=["color", "gray"], supports=["canonicalLinkHeader", "rotationArbitrary", "profileLinkHeader", "http://example.com/feature/"], service=[ {"@context": "http://iiif.io/api/annex/service/physdim/1/context.json", "profile": "http://iiif.io/api/annex/service/physdim", "physicalScale": 0.0025, "physicalUnits": "in"}, {"@context": "http://geojson.org/contexts/geojson-base.jsonld", "@id": "http://www.example.org/geojson/paris.json"}] ) reparsed_json = json.loads(i.as_json()) example_json = json.load( open('tests/testdata/info_json_2_1/info_from_spec_section_5_6.json')) self.maxDiff = 4000 self.assertEqual(reparsed_json, example_json)
def image_information_response(self): """Parse image information request and create response.""" dr = degraded_request(self.identifier) if (dr): self.logger.info("image_information: degraded %s -> %s" % (self.identifier, dr)) self.degraded = self.identifier self.identifier = dr else: self.logger.info("image_information: %s" % (self.identifier)) # get size self.manipulator.srcfile = self.file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image info = { 'tile_height': self.config.tile_height, 'tile_width': self.config.tile_width, 'scale_factors': self.config.scale_factors } # calculate scale factors if not hard-coded if ('auto' in self.config.scale_factors): info['scale_factors'] = self.manipulator.scale_factors( self.config.tile_width, self.config.tile_height) i = IIIFInfo(conf=info, api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height if (self.api_version >= '2.0'): # FIXME - should come from manipulator i.qualities = ["default", "color", "gray"] else: # FIXME - should come from manipulator i.qualities = ["native", "color", "gray"] i.formats = ["jpg", "png"] # FIXME - should come from manipulator if (self.auth): self.auth.add_services(i) return self.make_response( i.as_json(), headers={"Content-Type": self.json_mime_type})
def image_information_response(self): """Parse image information request and create response.""" dr = degraded_request(self.identifier) if dr: self.logger.info("image_information: degraded %s -> %s" % (self.identifier, dr)) self.degraded = self.identifier self.identifier = dr else: self.logger.info("image_information: %s" % (self.identifier)) # get size self.manipulator.srcfile = self.file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image info = { "tile_height": self.config.tile_height, "tile_width": self.config.tile_width, "scale_factors": self.config.scale_factors, } # calculate scale factors if not hard-coded if "auto" in self.config.scale_factors: info["scale_factors"] = self.manipulator.scale_factors(self.config.tile_width, self.config.tile_height) i = IIIFInfo(conf=info, api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height if self.api_version >= "2.0": # FIXME - should come from manipulator i.qualities = ["default", "color", "gray"] else: # FIXME - should come from manipulator i.qualities = ["native", "color", "gray"] i.formats = ["jpg", "png"] # FIXME - should come from manipulator if self.auth: self.auth.add_services(i) return self.make_response(i.as_json(), headers={"Content-Type": self.json_mime_type})
def test02_scale_factor(self): ir = IIIFInfo(width=199,height=299,tile_width=100,tile_height=100,scale_factors=[1,2], api_version='2.1') #self.assertRegexpMatches( ir.as_json(validate=False), r'"scaleFactors": \[\s*1' ) #,\s*2\s*]' ) #no assertRegexpMatches in 2.6 json = ir.as_json(validate=False) self.assertTrue( re.search(r'"width": 100',json) ) self.assertTrue( re.search(r'"scaleFactors": \[\s*1',json) )
def do_GET_body(self): iiif=self.iiif if (len(self.path)>1024): raise IIIFError(code=414, text="URI Too Long: Max 1024 chars, got %d\n" % len(self.path)) #print "GET " + self.path try: iiif.parse_url(self.path) except IIIFRequestBaseURI as e: info_uri = self.server_and_prefix + '/' + urllib.quote(self.iiif.identifier) + '/info.json' raise IIIFError(code=303, headers={'Location': info_uri}) except IIIFError as e: # Pass through raise e except Exception as e: # Something completely unexpected => 500 raise IIIFError(code=500, text="Internal Server Error: unexpected exception parsing request (" + str(e) + ")") # URL path parsed OK, now determine how to handle request if (re.match('[\w\.\-]+$',iiif.identifier)): file = os.path.join(IIIFRequestHandler.IMAGE_DIR,iiif.identifier) if (not os.path.isfile(file)): images_available="" for image_file in os.listdir(IIIFRequestHandler.IMAGE_DIR): if (os.path.isfile(os.path.join(IIIFRequestHandler.IMAGE_DIR,image_file))): images_available += " "+image_file+"\n" raise IIIFError(code=404,parameter="identifier", text="Image resource '"+iiif.identifier+"' not found. Local image files available:\n" + images_available) else: raise IIIFError(code=404,parameter="identifier", text="Image resource '"+iiif.identifier+"' not found. Only local test images and http: URIs for images are supported.\n") # self.compliance_level=self.manipulator.complianceLevel # Do we have auth? if (self.auth_type == 'basic'): auth_map = parse_authorization_header(self.headers.get('Authorization','')) if (auth_map and auth_map['type']=='basic' and auth_map['username']+':'+auth_map['password']==IIIFRequestHandler.USER_PASS): # authz, continue pass else: # failed, send 401 with null image info, no service link for auth as # basic auth is all done via the WWW-Authenticate header i = IIIFInfo(api_version=self.api_version) i.identifier = 'null' i.width = 0 i.height = 0 return self.send_json_response(json=i.as_json(),status=401) if (self.iiif.info): # get size self.manipulator.srcfile=file self.manipulator.do_first() # most of info.json comes from config, a few things specific to image i = IIIFInfo(conf=IIIFRequestHandler.INFO,api_version=self.api_version) i.server_and_prefix = self.server_and_prefix i.identifier = self.iiif.identifier i.width = self.manipulator.width i.height = self.manipulator.height i.qualities = [ "native", "color" ] #FIXME - should come from manipulator i.formats = [ "jpg", "png" ] #FIXME - should come from manipulator return self.send_json_response(i.as_json(),200) else: if (self.api_version<'2.0' and self.iiif.format is None and 'Accept' in self.headers): # In 1.0 and 1.1 conneg was specified as an alternative to format, see: # http://iiif.io/api/image/1.0/#format # http://iiif.io/api/image/1.1/#parameters-format formats = { 'image/jpeg': 'jpg', 'image/tiff': 'tif', 'image/png': 'png', 'image/gif': 'gif', 'image/jp2': 'jps', 'application/pdf': 'pdf' } accept = do_conneg( self.headers['Accept'], formats.keys() ) # Ignore Accept header if not recognized, should this be an error instead? if (accept in formats): self.iiif.format = formats[accept] (outfile,mime_type)=self.manipulator.derive(file,iiif) return self.send_good_response(open(outfile,'r'),mime_type)
def do_GET(self): """Implement the HTTP GET method The bulk of this code is wrapped in a big try block and anywhere within the code may raise an IIIFError which then results in an IIIF error response (section 5 of spec). """ self.compliance_level=None # We take prefix to see whe implementation to use, / is special info if (self.path == '/'): return self.send_top_level_index_page() # Test code... m = re.match(r'/response_test/(\d\d\d)$', self.path) if (m): status=int(m.group(1)) # WHAT DOES NULL info.json look like? # https://github.com/IIIF/auth/issues/2 self.api_version='2.0' i = IIIFInfo(api_version=self.api_version) i.identifier = 'abc' i.width = 0 i.height = 0 i.service = { '@context': 'http://example.org/auth_context.json', '@id': "http://example.com/authn_here", 'label': "Authenticate here" } return self.send_json_response(json=i.as_json(),status=status) # Is this a request for a prefix index page? m = re.match(r'/([\w\._]+)$', self.path) if (m and m.group(1) in IIIFRequestHandler.MANIPULATORS): return self.send_prefix_index_page(m.group(1)) # Is this a request for a test page? m = re.match(r'/([\w\._]+)$', self.path) if (m): page_path = os.path.join(IIIFRequestHandler.PAGES_DIR,m.group(1)) if (os.path.isfile(page_path)): return self.send_html_file(page_path) # Now assume we have an iiif request m = re.match(r'/([\w\._]+)/(.*)$', self.path) if (m): self.prefix = m.group(1) self.path = m.group(2) if (self.prefix in IIIFRequestHandler.MANIPULATORS): self.api_version = IIIFRequestHandler.MANIPULATORS[self.prefix]['api_version'] self.iiif = IIIFRequest(baseurl='/'+self.prefix+'/',api_version=self.api_version) self.manipulator = IIIFRequestHandler.MANIPULATORS[self.prefix]['klass'](api_version=self.api_version) self.auth_type = IIIFRequestHandler.MANIPULATORS[self.prefix]['auth_type'] else: # 404 - unrecognized prefix self.send_404_response("Not Found - prefix /%s/ is not known" % (self.prefix)) return else: # 404 - unrecognized path structure self.send_404_response("Not Found - path structure not recognized") return try: self.do_GET_body() except IIIFError as e: e.text += " Request parameters: "+str(self.iiif) self.write_error_response(e) except Exception as ue: # Anything else becomes a 500 Internal Server Error sys.stderr.write(str(ue)+"\n") self.write_error_response(IIIFError(code=500,text="Something went wrong... %s.\n"%(str(ue))))
def test02_scale_factor(self): """Test scale factor.""" ir = IIIFInfo(width=1, height=2, scale_factors=[ 1, 2], api_version='1.0') json = ir.as_json(validate=False) self.assertRegexpMatches(json, r'"scale_factors": \[\s*1')
def test20_write_full_example_in_spec(self): """Create example info.json in spec.""" i = IIIFInfo( api_version='3.0', id= "https://example.org/image-service/abcd1234/1E34750D-38DB-4825-A38A-B60A345E591C", # "protocol" : "http://iiif.io/api/image", width=6000, height=4000, sizes=[{ "width": 150, "height": 100 }, { "width": 600, "height": 400 }, { "width": 3000, "height": 2000 }], tiles=[{ "width": 512, "scaleFactors": [1, 2, 4] }, { "width": 1024, "height": 2048, "scaleFactors": [8, 16] }], attribution=[{ "@value": "<span>Provided by Example Organization</span>", "@language": "en" }, { "@value": "<span>Darparwyd gan Enghraifft Sefydliad</span>", "@language": "cy" }], logo={ "id": "https://example.org/image-service/logo/square/200,200/0/default.png", "service": [{ "id": "https://example.org/image-service/logo", "profile": "level2", 'type': 'ImageService3' }] }, license=[ "https://example.org/rights/license1.html", "http://rightsstatements.org/vocab/InC-EDU/1.0/" ], profile="level1", extra_formats=["gif", "pdf"], extra_qualities=["color", "gray"], extra_features=[ "canonicalLinkHeader", "rotationArbitrary", "profileLinkHeader" ], service=[{ 'id': 'http://example.org/fix/me' }]) i.add_context("http://example.org/extension/context1.json") reparsed_json = json.loads(i.as_json()) example_json = json.load( self.open_testdata('info_from_spec_section_5_8.json')) self.maxDiff = 4000 self.assertEqual(reparsed_json, example_json)
def test02_scale_factor(self): ir = IIIFInfo(width=199,height=299,tile_width=100,tile_height=100,scale_factors=[1,2]) #self.assertRegexpMatches( ir.as_json(validate=False), r'"scaleFactors": \[\s*1' ) #,\s*2\s*]' ) #no assertRegexpMatches in 2.6 json = ir.as_json(validate=False) self.assertTrue( re.search(r'"width": 100',json) ) self.assertTrue( re.search(r'"scaleFactors": \[\s*1',json) )