Ejemplo n.º 1
0
 def test02_generate(self):
     tmp = tempfile.mkdtemp()
     try:
         s=IIIFStatic( src='testimages/starfish_1500x2000.png', dst=tmp, tilesize=1024, api_version='1', dryrun=True )
         with capture_stdout() as capturer:
             s.generate(identifier='a')
         self.assertTrue( re.search(' / a/1024,1024,476,976/476,976/0/native.jpg', capturer.result ))
         self.assertTrue( re.search(' / a/full/1,1/0/native.jpg', capturer.result ))
     finally:
         shutil.rmtree(tmp) 
Ejemplo n.º 2
0
 def test02_parse_extra(self):
     """Test parse_extra."""
     s = IIIFStatic()
     r = s.parse_extra('/0,0,100,100/10,10/0/default.jpg')
     self.assertEqual(r.region_xywh, [0, 0, 100, 100])
     r = s.parse_extra('full/full/0/default.jpg')
     self.assertEqual(r.region_full, True)
     # Error cases
     self.assertRaises(IIIFRequestError, s.parse_extra, '')
     self.assertRaises(IIIFRequestError, s.parse_extra, '/bad/')
     self.assertRaises(IIIFStaticError, s.parse_extra, 'info.json')
Ejemplo n.º 3
0
 def generate_deep_zoom(self, jekyll_site_dir):
     self.log_status('Downloading deep zoom images')
     imagedir = os.path.join(jekyll_site_dir, self.image_dir)
     # If exporting to GitHub, add the repo name to the path.
     _prefix = os.path.join(os.sep, self.github_repo, self.image_dir) if self.github_repo else os.path.join(os.sep, self.image_dir)
     staticgen = IIIFStatic(dst=imagedir, prefix=_prefix)
     for teipage in self.tei.page_list:
         for graphic in teipage.graphics:
             if graphic.rend == 'full':
                 imgsrc = os.path.join(jekyll_site_dir, graphic.url)
                 iiif_img = IIIFImageClient.init_from_url(graphic.url)
                 staticgen.generate(imgsrc, identifier=iiif_img.image_id)
Ejemplo n.º 4
0
 def test03_generate_tile(self):
     # most tested via other calls, make sure zero size skip works
     tmp1 = tempfile.mkdtemp()
     os.mkdir( os.path.join(tmp1,'a') )
     try:
         s=IIIFStatic( dst=tmp1, tilesize=512, api_version='2.0' )
         s.identifier = 'fgh'
         s.src = 'testimages/starfish_1500x2000.png'
         with capture_stdout() as capturer:
             s.generate_tile( region='full', size=[0,1] )
         self.assertTrue( re.search(r'zero size, skipped', capturer.result) )
     finally:
         shutil.rmtree(tmp1)
Ejemplo n.º 5
0
def handler(event, context):
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']
        inbound = '/tmp/inbound/%s' % key
        outbound = '/tmp/outbound/%s' % key
        s3_client.download_file(bucket, key, inbound)

        # TODO: Add prefix
        sg = IIIFStatic(dst=outbound, tilesize=512, api_version='2.0')
        sg.generate(inbound)
        for dirpath, dirnames, files in os.walk(outbound):
            for f in files:
                s3_client.upload_file(os.path.join(dirpath, f), destbucket, f)
Ejemplo n.º 6
0
def main():

    if (sys.version_info < (2,6)):
        sys.exit("This program requires python version 2.6 or later")
    
    # Options and arguments
    p = optparse.OptionParser(description='IIIF Image API static file generator',
                              usage='usage: %prog [options] file|path (-h for help)',
                              version='%prog '+__version__ )

    p.add_option('--dst', '-d', action='store', default='/tmp',
                 help="destination directory [/tmp default]")
    p.add_option('--tilesize', '-t', action='store', type='int', default=512,
                 help="tilesize in pixels [512 default]")
    p.add_option('--api-version', '--api','-a', action='store', default='1.1',
                 help="API version, may be 1.1 [default] or 2.0") 
    p.add_option('--dryrun', '-n', action='store_true',
                 help="do not write anything, say what would be done")
    p.add_option('--verbose', '-v', action='store_true',
                 help="verbose")

    (args, sources) = p.parse_args()

    logging.basicConfig( format='%(name)s: %(message)s',
                         level=( logging.INFO if (args.verbose) else logging.WARNING ) )
    logger = logging.getLogger(os.path.basename(__file__))

    if (len(sources)==0):
        logger.warn("no sources specified, nothing to do, bye! (-h for help)")
    else:
        for source in sources:
            # File or directory (or neither)?
            sg = IIIFStatic( dst=args.dst, tilesize=args.tilesize,
                             api_version=args.api_version, dryrun=args.dryrun )
            if (os.path.isfile(source)):
                logger.info("source file: %s" % (source))
                sg.generate(source)
            elif (os.path.isdir(source)):
                raise Exception("source dir: %s - FIXME: not yet supported" % (source))
            else:
                logger.warn("ignoring source '%s': neither file nor path" % (source))
Ejemplo n.º 7
0
 def test04_generate_tile(self):
     """Test generation of a tile."""
     # most tested via other calls, make sure zero size skip works
     tmp1 = tempfile.mkdtemp()
     os.mkdir(os.path.join(tmp1, 'a'))
     try:
         s = IIIFStatic(dst=tmp1, tilesize=512, api_version='2.0')
         s.identifier = 'fgh'
         s.src = 'testimages/starfish_1500x2000.png'
         with MyLogCapture('iiif.static') as lc:
             s.generate_tile(region='full', size=[0, 1])
         self.assertTrue(re.search(r'zero size, skipped', lc.all_msgs))
     finally:
         shutil.rmtree(tmp1)
Ejemplo n.º 8
0
 def test02_generate(self):
     # dryrun covers most
     tmp1 = tempfile.mkdtemp()
     os.mkdir( os.path.join(tmp1,'a') )
     try:
         s=IIIFStatic( dst=tmp1, tilesize=512, api_version='1.1', dryrun=True )
         with capture_stdout() as capturer:
             s.generate( src='testimages/starfish_1500x2000.png', identifier='a' )
         self.assertTrue( re.search(' / a/info.json', capturer.result ))
         self.assertTrue( re.search(' / a/1024,1536,476,464/476,/0/native.jpg', capturer.result ))
         self.assertTrue( re.search(' / a/full/1,/0/native.jpg', capturer.result ))
     finally:
         shutil.rmtree(tmp1)
     # real write 
     tmp2 = tempfile.mkdtemp()
     try:
         s=IIIFStatic( dst=tmp2, tilesize=1024, api_version='2.0' )
         with capture_stdout() as capturer:
             s.generate( src='testimages/starfish_1500x2000.png', identifier='b' )
         self.assertTrue( os.path.isfile(os.path.join(tmp2,'b/info.json')) )
         self.assertTrue( os.path.isfile(os.path.join(tmp2,'b/1024,1024,476,976/476,/0/default.jpg')) )
         self.assertTrue( os.path.isfile(os.path.join(tmp2,'b/full/1,/0/default.jpg')) )
     finally:
         shutil.rmtree(tmp2)
Ejemplo n.º 9
0
 def test07_setup_destination(self):
     """Test setip_destination."""
     s = IIIFStatic()
     # no dst
     self.assertRaises(Exception, s.setup_destination)
     # now really create dir
     tmp = tempfile.mkdtemp()
     try:
         # dst and no identifier
         s.src = 'a/b.ext'
         s.dst = os.path.join(tmp, 'xyz')
         s.identifier = None
         s.setup_destination()
         self.assertTrue(os.path.isdir(tmp))
         self.assertTrue(os.path.isdir(s.dst))
         self.assertTrue(os.path.isdir(os.path.join(s.dst, 'b')))
         self.assertEqual(s.identifier, 'b')
         # dst and identifier
         s.src = 'a/b.ext'
         s.dst = os.path.join(tmp, 'zyx')
         s.identifier = 'c'
         s.setup_destination()
         self.assertTrue(os.path.isdir(s.dst))
         self.assertTrue(os.path.isdir(os.path.join(s.dst, 'c')))
         self.assertEqual(s.identifier, 'c')
         # dst path is file
         s.dst = os.path.join(tmp, 'exists1')
         open(s.dst, 'w').close()
         self.assertRaises(Exception, s.setup_destination)
         # dst and identifier, path is file
         s.identifier = 'exists2'
         s.dst = tmp
         open(os.path.join(s.dst, s.identifier), 'w').close()
         self.assertRaises(Exception, s.setup_destination)
         # dst and identifier, both dirs exist and OK
         s.dst = tmp
         s.identifier = 'id1'
         os.mkdir(os.path.join(s.dst, s.identifier))
         s.setup_destination()  # nothing created, no exception
     finally:
         shutil.rmtree(tmp)
Ejemplo n.º 10
0
 def test04_setup_destination(self):
     s = IIIFStatic()
     # no dst
     self.assertRaises(Exception, s.setup_destination)
     # now really create dir
     tmp = tempfile.mkdtemp()
     try:
         # dst and no identifier
         s.dst = os.path.join(tmp, 'xyz')
         s.identifier = None
         s.setup_destination()
         self.assertTrue(os.path.isdir(s.dst))
         self.assertEqual(s.outd, tmp)
         self.assertEqual(s.identifier, 'xyz')
         # dst and identifier
         s.dst = os.path.join(tmp, 'zyx')
         s.identifier = 'abc'
         s.setup_destination()
         self.assertTrue(os.path.isdir(s.dst))
         self.assertTrue(os.path.isdir(os.path.join(s.dst, 'abc')))
         self.assertEqual(s.outd, s.dst)
         self.assertEqual(s.identifier, 'abc')
         # dst path is file
         s.dst = os.path.join(tmp, 'exists1')
         open(s.dst, 'w').close()
         self.assertRaises(Exception, s.setup_destination)
         # dst and identifier, path is file
         s.identifier = 'exists2'
         s.dst = tmp
         open(os.path.join(s.dst, s.identifier), 'w').close()
         self.assertRaises(Exception, s.setup_destination)
         # dst and identifier, both dirs exist and OK
         s.outd = None
         s.dst = tmp
         s.identifier = 'id1'
         os.mkdir(os.path.join(s.dst, s.identifier))
         s.setup_destination()
         self.assertEqual(s.outd, tmp)
     finally:
         shutil.rmtree(tmp)
Ejemplo n.º 11
0
class ImageProcessor:
    """Manage generation of IIIF resources from manuscript dictionary."""
    _failed = []

    def __init__(self, generate_image_pyramid: bool = True):
        """Inits ImageProcessor and configures manifest factory.

        :param generate_image_pyramid:if True, pyramid map will be generated. Else, only manifest is generated.
        """
        if not os.path.exists(MANIFEST_OUTPUT_DIR):
            os.makedirs(MANIFEST_OUTPUT_DIR)
        self._manifest_factory = ManifestFactory()
        self._manifest_factory.set_base_prezi_uri(MANIFEST_BASE_URL)
        self._manifest_factory.set_base_prezi_dir(MANIFEST_OUTPUT_DIR)
        self._manifest_factory.set_base_image_uri(IMAGE_BASE_URL)
        self._manifest_factory.set_iiif_image_info(2.0, 1)  # Version, ComplianceLevel

        self._image_reader = ImageReader(IMAGE_SOURCE_DIR)

        if generate_image_pyramid:
            self._tile_generator = IIIFStatic(dst=IMAGE_FILE_OUTPUT_DIR, prefix=IMAGE_BASE_URL)

        self._generate_images = generate_image_pyramid

    def generate_iiif_resources(self, manuscript_data: Iterable[ManuscriptRow]) -> None:
        """Generate static IIIF resources for every manuscript record.

        IIIF resources include image pyramid and manifests.

        :param manuscript_data:list of dictionaries containing manuscript metadata.
        :return:None
        """
        for manuscript in manuscript_data:
            self._process_manuscript(manuscript)

        if self._failed:
            print("Errors encountered processing following manuscripts: ")
            print(*self._failed, sep=", ")

    def _process_manuscript(self, manuscript: ManuscriptRow) -> None:
        mhs_number = manuscript.get(ColumnKeys.MHS_NUMBER)

        # noinspection PyBroadException
        try:
            if Path(os.path.join(MANIFEST_OUTPUT_DIR, f"{mhs_number}.json")).is_file():
                print(f"{mhs_number} already processed. Skipping")
                return

            manifest = self._create_manifest(manuscript)

            self._add_canvases(manuscript, manifest)

            manifest.toFile(compact=False)
        except Exception as e:
            print(f"**Error processing {mhs_number}. {e}")
            self._failed.append(mhs_number)

    def _create_manifest(self, manuscript: ManuscriptRow) -> Manifest:
        mhs_number = manuscript.get(ColumnKeys.MHS_NUMBER)
        alternative_name = manuscript.get(ColumnKeys.ALTERNATIVE_NAME)

        print(f"creating manifest for {mhs_number}")
        manifest = self._manifest_factory.manifest(label=f"{mhs_number} - {alternative_name}", ident=mhs_number)

        # add all non-empty fields as metadata (excluding "No" field as this is just internal id)
        manifest.set_metadata({k: v for (k, v) in manuscript.items() if v and k != ColumnKeys.NO
                               and k != ColumnKeys.COMMENTS})
        manifest.description = manuscript.get(ColumnKeys.COMMENTS)
        return manifest

    def _add_canvases(self, manuscript: ManuscriptRow, manifest: Manifest) -> None:
        manuscript_images = self._image_reader.get_files_for_manuscript(manuscript)
        mhs_number = manuscript.get(ColumnKeys.MHS_NUMBER)

        image_count = len(manuscript_images)

        print(f"creating {image_count} canvases for {mhs_number}..")

        seq = manifest.sequence()

        for p in range(image_count):
            image_id = f"{mhs_number}-{p}"
            start = time.time()
            print(f"processing {image_id}..")
            cvs = seq.canvas(ident=image_id, label=f"Page {p}")

            # Create an annotation on the Canvas
            annotation = cvs.annotation(ident=f"page-{p}")

            # set source of image data
            img = annotation.image(image_id, iiif=True)

            # Set image height and width, and canvas to same dimensions
            image_file = manuscript_images[p]
            img.set_hw_from_file(image_file)
            cvs.height = img.height
            cvs.width = img.width
            self._generate_image_pyramid(image_file, image_id, img.width, img.height)

            end = time.time()
            print(f"processed {image_id} in {end - start} secs")

    def _generate_image_pyramid(self, image_file: str, image_id: str, width: int, height: int) -> None:
        if not self._generate_images:
            return

        self._tile_generator.generate(src=image_file, identifier=image_id)

        # generate a 90-wide thumb for UV (see: https://github.com/UniversalViewer/universalviewer/issues/102)
        self._tile_generator.generate_tile("full", [90, None])

        # generate a 1000-wide /full/ image for UV download
        h = 1000 / (width / height)
        self._tile_generator.generate_tile("full", [1000, h])

        # generate a max-width /full/ image for UV download
        self._tile_generator.generate_tile("full", [width, None])
Ejemplo n.º 12
0
def main():
    """Parse arguments, instantiate IIIFStatic, run."""
    if sys.version_info < (2, 6):
        sys.exit("This program requires python version 2.6 or later")

    # Options and arguments
    p = optparse.OptionParser(
        description="IIIF Image API static file generator",
        usage="usage: %prog [options] file [[file2..]] (-h for help)",
        version="%prog " + __version__,
    )

    p.add_option("--dst", "-d", action="store", default="/tmp", help="destination directory [default '%default']")
    p.add_option(
        "--tilesize", "-t", action="store", type="int", default=512, help="tilesize in pixels [default %default]"
    )
    p.add_option(
        "--api-version",
        "--api",
        "-a",
        action="store",
        default="2.0",
        help="API version, may be 1.1 or 2.0 [default %default]",
    )
    p.add_option(
        "--prefix",
        "-p",
        action="store",
        default=None,
        help="URI prefix for where the images will be served from (default '%default'). "
        "An empty prefix may be OK if the HTML page including the image shares the "
        "the same root on the same server as the images, otherwise a full URL should "
        "be specified. This is used to construct the @id in the info.json",
    )
    p.add_option(
        "--identifier",
        "-i",
        action="store",
        default=None,
        help="Identifier for the image that will be used in place of the filename "
        "(minus extension). Notes that this option cannot be used if more than "
        "one image file is to be processed",
    )
    p.add_option(
        "--write-html",
        action="store",
        default=None,
        help="Write HTML page to the specified directory using the 'identifier.html' "
        "as the filename. HTML will launch OpenSeadragon for this image and to "
        "display some of information about info.json and tile locations. HTML will "
        "assume OpenSeadragon at relative path osd/openseadragon.min.js and interface "
        "icons in osd/images. The --include-osd flag is also specified then "
        "OpenSeadragon will be copied to these locations",
    )
    p.add_option("--include-osd", action="store_true", help="Include OpenSeadragon files with --write-html flag")
    p.add_option("--dryrun", "-n", action="store_true", help="do not write anything, say what would be done")
    p.add_option("--verbose", "-v", action="store_true", help="verbose")

    (opt, sources) = p.parse_args()

    logging.basicConfig(format="%(name)s: %(message)s", level=(logging.INFO if (opt.verbose) else logging.WARNING))
    logger = logging.getLogger(os.path.basename(__file__))

    if len(sources) == 0:
        logger.warn("No sources specified, nothing to do, bye! (-h for help)")
    elif len(sources) > 1 and opt.identifier:
        logger.warn("Cannot use --identifier/-i option with multiple sources, aborting.")
    else:
        sg = IIIFStatic(
            dst=opt.dst, tilesize=opt.tilesize, api_version=opt.api_version, dryrun=opt.dryrun, prefix=opt.prefix
        )
        for source in sources:
            # File or directory (or neither)?
            if os.path.isfile(source):
                logger.info("source file: %s" % (source))
                sg.generate(source, identifier=opt.identifier)
                if opt.write_html:
                    sg.write_html(opt.write_html, opt.include_osd)
            elif os.path.isdir(source):
                logger.warn("Ignoring source '%s': directory coversion not supported" % (source))
            else:
                logger.warn("Ignoring source '%s': neither file nor path" % (source))
Ejemplo n.º 13
0
 def test03_generate(self):
     """Test generation of a static image."""
     # dryrun covers most
     tmp1 = tempfile.mkdtemp()
     os.mkdir(os.path.join(tmp1, 'a'))
     try:
         # no canonical syntax with osd_version='1.0.0'
         s = IIIFStatic(dst=tmp1, tilesize=512,
                        api_version='1.1', osd_version='1.0.0', dryrun=True)
         with MyLogCapture('iiif.static') as lc:
             s.generate(src='testimages/starfish_1500x2000.png',
                        identifier='a')
         self.assertTrue(re.search(' / a/info.json', lc.all_msgs))
         self.assertTrue(
             re.search(' / a/1024,1536,476,464/476,464/0/native.jpg', lc.all_msgs))
         # largest full region (and symlink from w,h)
         self.assertTrue(
             re.search(' / a/full/375,500/0/native.jpg', lc.all_msgs))
         # smallest full region
         self.assertTrue(
             re.search(' / a/full/1,1/0/native.jpg', lc.all_msgs))
         # v2.0
         s = IIIFStatic(dst=tmp1, tilesize=512,
                        api_version='2.0', dryrun=True)
         with MyLogCapture('iiif.static') as lc:
             s.generate(src='testimages/starfish_1500x2000.png',
                        identifier='a')
         self.assertTrue(re.search(' / a/info.json', lc.all_msgs))
         self.assertTrue(
             re.search(' / a/1024,1536,476,464/476,/0/default.jpg', lc.all_msgs))
         # largest full region (and symlink from w,h)
         self.assertTrue(
             re.search(' / a/full/375,/0/default.jpg', lc.all_msgs))
         self.assertTrue(
             re.search(' / a/full/375,500 -> a/full/375,', lc.all_msgs))
         # smallest full region
         self.assertTrue(
             re.search(' / a/full/1,/0/default.jpg', lc.all_msgs))
         self.assertTrue(
             re.search(' / a/full/1,1 -> a/full/1,', lc.all_msgs))
     finally:
         shutil.rmtree(tmp1)
     # real write
     tmp2 = tempfile.mkdtemp()
     try:
         s = IIIFStatic(dst=tmp2, tilesize=1024, api_version='2.0')
         with MyLogCapture('iiif.static') as lc:
             s.generate(src='testimages/starfish_1500x2000.png',
                        identifier='b')
         self.assertTrue(os.path.isfile(os.path.join(tmp2, 'b/info.json')))
         self.assertTrue(os.path.isfile(os.path.join(
             tmp2, 'b/1024,1024,476,976/476,/0/default.jpg')))
         self.assertTrue(os.path.isfile(
             os.path.join(tmp2, 'b/full/1,/0/default.jpg')))
     finally:
         shutil.rmtree(tmp2)
Ejemplo n.º 14
0
 def test02_get_osd_config(self):
     """Test get_osd_config."""
     s = IIIFStatic()
     self.assertEqual(s.get_osd_config('2.0.0')['use_canonical'], True)
     self.assertRaises(IIIFStaticError, s.get_osd_config, 'abc')
     self.assertRaises(IIIFStaticError, s.get_osd_config, '0.0.0')
Ejemplo n.º 15
0
 def test08_write_html(self):
     """Test write_html."""
     s = IIIFStatic()
     # bad output dir
     self.assertRaises(Exception, s.write_html,
                       '/tmp/path_does_no_exist_(i_hope)')
     # write to good path
     tmp = tempfile.mkdtemp()
     s.identifier = 'abc1'
     s.write_html(tmp)
     self.assertTrue(os.path.isfile(os.path.join(tmp, 'abc1.html')))
     # write to subdir of good path
     tmp = tempfile.mkdtemp()
     s.identifier = 'abc2'
     tmp2 = os.path.join(tmp, 'xyz')
     s.write_html(tmp2)
     self.assertTrue(os.path.isfile(os.path.join(tmp2, 'abc2.html')))
     # write to good path with osd
     tmp = tempfile.mkdtemp()
     s.identifier = 'abc3'
     s.write_html(tmp, include_osd=True)
     self.assertTrue(os.path.isfile(os.path.join(tmp, 'abc3.html')))
     self.assertTrue(os.path.isfile(os.path.join(
         tmp, 'openseadragon200/openseadragon.min.js')))
     self.assertTrue(s.copied_osd)
     # add another, with osd already present (and marked as such)
     s.identifier = 'abc4'
     with LogCapture('iiif.static') as lc:
         s.write_html(tmp, include_osd=True)
     self.assertTrue(os.path.isfile(os.path.join(tmp, 'abc4.html')))
     self.assertTrue(os.path.isfile(os.path.join(
         tmp, 'openseadragon200/openseadragon.min.js')))
     self.assertTrue(s.copied_osd)
     self.assertEqual(lc.records[-1].msg, 'OpenSeadragon already copied')
     # add yet another, with osd already present (but not marked)
     s.identifier = 'abc5'
     s.copied_osd = False
     with LogCapture('iiif.static') as lc:
         s.write_html(tmp, include_osd=True)
     self.assertTrue(os.path.isfile(os.path.join(tmp, 'abc5.html')))
     self.assertTrue(os.path.isfile(os.path.join(
         tmp, 'openseadragon200/openseadragon.min.js')))
     self.assertTrue(s.copied_osd)
     self.assertTrue(re.search(r'OpenSeadragon images directory .* already exists',
                               lc.records[-1].msg))
     # add another but with a prefix
     s.identifier = 'abc6'
     s.prefix = 'z/y/x'
     s.copied_osd = False
     s.write_html(tmp, include_osd=True)
     html_file = os.path.join(tmp, 'abc6.html')
     self.assertTrue(os.path.isfile(html_file))
     with open(html_file, 'r') as x:
         html = x.read()
     self.assertTrue(re.search(r'z/y/x/abc6/info.json', html))
     # bad write to existing path
     tmp = tempfile.mkdtemp()
     tmp2 = os.path.join(tmp, 'file')
     open(tmp2, 'w').close()
     s.identifier = 'abc4'
     self.assertRaises(Exception, s.write_html, tmp2)
Ejemplo n.º 16
0
def main():
    """Parse arguments, instantiate IIIFStatic, run."""
    if (sys.version_info < (2, 6)):
        sys.exit("This program requires python version 2.6 or later")

    # Options and arguments
    p = optparse.OptionParser(description='IIIF Image API static file generator',
                              usage='usage: %prog [options] file [[file2..]] (-h for help)',
                              version='%prog ' + __version__)

    p.add_option('--dst', '-d', action='store', default='/tmp',
                 help="Destination directory for images [default '%default']")
    p.add_option('--tilesize', '-t', action='store', type='int', default=512,
                 help="Tilesize in pixels [default %default]")
    p.add_option('--api-version', '--api', '-a', action='store', default='2.1',
                 help="API version, may be 1.1, 2.0 or 2.1 [default %default]")
    p.add_option('--prefix', '-p', action='store', default=None,
                 help="URI prefix for where the images will be served from (default '%default'). "
                      "An empty prefix may be OK if the HTML page including the image shares the "
                      "the same root on the same server as the images, otherwise a full URL should "
                      "be specified. This is used to construct the @id in the info.json")
    p.add_option('--identifier', '-i', action='store', default=None,
                 help="Identifier for the image that will be used in place of the filename "
                      "(minus extension). Notes that this option cannot be used if more than "
                      "one image file is to be processed")
    p.add_option('--write-html', action='store', default=None,
                 help="Write HTML page to the specified directory using the 'identifier.html' "
                      "as the filename. HTML will launch OpenSeadragon for this image and to "
                      "display some of information about info.json and tile locations. HTML will "
                      "assume OpenSeadragon at relative path openseadragonVVV/openseadragon.min.js "
                      "and user-interface icons in openseadragonVVV/images, where VVV are the "
                      "three parts of the version number. The --include-osd flag is also specified "
                      "then OpenSeadragon will be copied to these locations")
    p.add_option('--include-osd', action='store_true',
                 help="Include OpenSeadragon files with --write-html flag")
    p.add_option('--osd-version', action='store', default='2.0.0',
                 help="Generate static images for older versions of OpenSeadragon. Use of versions "
                      "prior to 1.2.1 will force use of /w,h/ for size parameter instead of /w,/. "
                      "Likely useful only in combination with --api-version=1.1 "
                      "[default %default]")
    p.add_option('--osd-width', action='store', type='int', default='500',
                 help="Width of OpenSeadragon pane in pixels. Applies only with "
                      "--write-html [default %default]")
    p.add_option('--osd-height', action='store', type='int', default='500',
                 help="Height of OpenSeadragon pane in pixels. Applies only with "
                      "--write-html [default %default]")
    p.add_option('--generator', action='store_true', default=False,
                 help="Use named generator modules in iiif.generators package instead "
                      "of a starting image [default %default]")
    p.add_option('--max-image-pixels', action='store', type='int', default=0,
                 help="Set the maximum number of pixels in an image. A non-zero value "
                      "will set a hard limit on the image size. If left unset then the "
                      "default configuration of the Python Image Libary (PIL) will give "
                      "a DecompressionBombWarning if the image size exceeds a default "
                      "maximum, but otherwise continue as normal")
    p.add_option('--dryrun', '-n', action='store_true',
                 help="Do not write anything, say what would be done")
    p.add_option('--quiet', '-q', action='store_true',
                 help="Quite (no output unless there is a warning/error)")
    p.add_option('--verbose', '-v', action='store_true',
                 help="Verbose")

    (opt, sources) = p.parse_args()

    level = logging.DEBUG if (opt.verbose) else \
        logging.WARNING if (opt.quiet) else logging.INFO
    logging.basicConfig(format='%(name)s: %(message)s',
                        level=level)
    logger = logging.getLogger(os.path.basename(__file__))

    if (not opt.write_html and opt.include_osd):
        logger.warn(
            "--include-osd has no effect without --write-html, ignoring")
    if (len(sources) == 0):
        logger.warn("No sources specified, nothing to do, bye! (-h for help)")
    elif (len(sources) > 1 and opt.identifier):
        logger.error(
            "Cannot use --identifier/-i option with multiple sources, aborting.")
    else:
        try:
            sg = IIIFStatic(dst=opt.dst, tilesize=opt.tilesize,
                            api_version=opt.api_version, dryrun=opt.dryrun,
                            prefix=opt.prefix, osd_version=opt.osd_version,
                            generator=opt.generator,
                            max_image_pixels=opt.max_image_pixels)
            for source in sources:
                # File or directory (or neither)?
                if (os.path.isfile(source) or opt.generator):
                    logger.info("source file: %s" % (source))
                    sg.generate(source, identifier=opt.identifier)
                    if (opt.write_html):
                        sg.write_html(html_dir=opt.write_html, include_osd=opt.include_osd,
                                      osd_width=opt.osd_width, osd_height=opt.osd_height)
                elif (os.path.isdir(source)):
                    logger.warn(
                        "Ignoring source '%s': directory coversion not supported" % (source))
                else:
                    logger.warn(
                        "Ignoring source '%s': neither file nor path" % (source))
        except (IIIFStaticError, IIIFError) as e:
            # catch known errors and report nicely...
            logger.error("Error: " + str(e))
Ejemplo n.º 17
0
 def test08_setup_destination(self):
     """Test setip_destination."""
     s = IIIFStatic()
     # no dst
     self.assertRaises(Exception, s.setup_destination)
     # now really create dir
     tmp = tempfile.mkdtemp()
     try:
         # no dst
         s.identifier = 'a'
         s.dst = None
         self.assertRaises(IIIFStaticError, s.setup_destination)
         # dst and no identifier
         s.src = 'a/b.ext'
         s.dst = os.path.join(tmp, 'xyz')
         s.identifier = None
         s.setup_destination()
         self.assertTrue(os.path.isdir(tmp))
         self.assertTrue(os.path.isdir(s.dst))
         self.assertTrue(os.path.isdir(os.path.join(s.dst, 'b')))
         self.assertEqual(s.identifier, 'b')
         # dst and identifier
         s.src = 'a/b.ext'
         s.dst = os.path.join(tmp, 'zyx')
         s.identifier = 'c'
         s.setup_destination()
         self.assertTrue(os.path.isdir(s.dst))
         self.assertTrue(os.path.isdir(os.path.join(s.dst, 'c')))
         self.assertEqual(s.identifier, 'c')
         # dst path is file
         s.dst = os.path.join(tmp, 'exists1')
         open(s.dst, 'w').close()
         self.assertRaises(Exception, s.setup_destination)
         # dst and identifier, path is file
         s.identifier = 'exists2'
         s.dst = tmp
         open(os.path.join(s.dst, s.identifier), 'w').close()
         self.assertRaises(Exception, s.setup_destination)
         # dst and identifier, both dirs exist and OK
         s.dst = tmp
         s.identifier = 'id1'
         os.mkdir(os.path.join(s.dst, s.identifier))
         s.setup_destination()  # nothing created, no exception
     finally:
         shutil.rmtree(tmp)
Ejemplo n.º 18
0
def main():
    """Parse arguments, instantiate IIIFStatic, run."""
    if (sys.version_info < (2, 6)):
        sys.exit("This program requires python version 2.6 or later")

    # Options and arguments
    p = optparse.OptionParser(
        description='IIIF Image API static file generator',
        usage='usage: %prog [options] file [[file2..]] (-h for help)',
        version='%prog ' + __version__)

    p.add_option('--dst',
                 '-d',
                 action='store',
                 default='/tmp',
                 help="Destination directory for images [default '%default']")
    p.add_option('--tilesize',
                 '-t',
                 action='store',
                 type='int',
                 default=512,
                 help="Tilesize in pixels [default %default]")
    p.add_option('--api-version',
                 '--api',
                 '-a',
                 action='store',
                 default='2.1',
                 help="API version, may be 1.1, 2.0 or 2.1 [default %default]")
    p.add_option(
        '--prefix',
        '-p',
        action='store',
        default=None,
        help=
        "URI prefix for where the images will be served from (default '%default'). "
        "An empty prefix may be OK if the HTML page including the image shares the "
        "the same root on the same server as the images, otherwise a full URL should "
        "be specified. This is used to construct the @id in the info.json")
    p.add_option(
        '--identifier',
        '-i',
        action='store',
        default=None,
        help=
        "Identifier for the image that will be used in place of the filename "
        "(minus extension). Notes that this option cannot be used if more than "
        "one image file is to be processed")
    p.add_option(
        '--write-html',
        action='store',
        default=None,
        help=
        "Write HTML page to the specified directory using the 'identifier.html' "
        "as the filename. HTML will launch OpenSeadragon for this image and to "
        "display some of information about info.json and tile locations. HTML will "
        "assume OpenSeadragon at relative path openseadragonVVV/openseadragon.min.js "
        "and user-interface icons in openseadragonVVV/images, where VVV are the "
        "three parts of the version number. The --include-osd flag is also specified "
        "then OpenSeadragon will be copied to these locations")
    p.add_option('--include-osd',
                 action='store_true',
                 help="Include OpenSeadragon files with --write-html flag")
    p.add_option(
        '--osd-version',
        action='store',
        default='2.0.0',
        help=
        "Generate static images for older versions of OpenSeadragon. Use of versions "
        "prior to 1.2.1 will force use of /w,h/ for size parameter instead of /w,/. "
        "Likely useful only in combination with --api-version=1.1 "
        "[default %default]")
    p.add_option(
        '--osd-width',
        action='store',
        type='int',
        default='500',
        help="Width of OpenSeadragon pane in pixels. Applies only with "
        "--write-html [default %default]")
    p.add_option(
        '--osd-height',
        action='store',
        type='int',
        default='500',
        help="Height of OpenSeadragon pane in pixels. Applies only with "
        "--write-html [default %default]")
    p.add_option(
        '--generator',
        action='store_true',
        default=False,
        help="Use named generator modules in iiif.generators package instead "
        "of a starting image [default %default]")
    p.add_option(
        '--max-image-pixels',
        action='store',
        type='int',
        default=0,
        help="Set the maximum number of pixels in an image. A non-zero value "
        "will set a hard limit on the image size. If left unset then the "
        "default configuration of the Python Image Libary (PIL) will give "
        "a DecompressionBombWarning if the image size exceeds a default "
        "maximum, but otherwise continue as normal")
    p.add_option('--dryrun',
                 '-n',
                 action='store_true',
                 help="Do not write anything, say what would be done")
    p.add_option('--quiet',
                 '-q',
                 action='store_true',
                 help="Quite (no output unless there is a warning/error)")
    p.add_option('--verbose', '-v', action='store_true', help="Verbose")

    (opt, sources) = p.parse_args()

    level = logging.DEBUG if (opt.verbose) else \
        logging.WARNING if (opt.quiet) else logging.INFO
    logging.basicConfig(format='%(name)s: %(message)s', level=level)
    logger = logging.getLogger(os.path.basename(__file__))

    if (not opt.write_html and opt.include_osd):
        logger.warn(
            "--include-osd has no effect without --write-html, ignoring")
    if (len(sources) == 0):
        logger.warn("No sources specified, nothing to do, bye! (-h for help)")
    elif (len(sources) > 1 and opt.identifier):
        logger.error(
            "Cannot use --identifier/-i option with multiple sources, aborting."
        )
    else:
        try:
            sg = IIIFStatic(dst=opt.dst,
                            tilesize=opt.tilesize,
                            api_version=opt.api_version,
                            dryrun=opt.dryrun,
                            prefix=opt.prefix,
                            osd_version=opt.osd_version,
                            generator=opt.generator,
                            max_image_pixels=opt.max_image_pixels)
            for source in sources:
                # File or directory (or neither)?
                if (os.path.isfile(source) or opt.generator):
                    logger.info("source file: %s" % (source))
                    sg.generate(source, identifier=opt.identifier)
                    if (opt.write_html):
                        sg.write_html(html_dir=opt.write_html,
                                      include_osd=opt.include_osd,
                                      osd_width=opt.osd_width,
                                      osd_height=opt.osd_height)
                elif (os.path.isdir(source)):
                    logger.warn(
                        "Ignoring source '%s': directory coversion not supported"
                        % (source))
                else:
                    logger.warn("Ignoring source '%s': neither file nor path" %
                                (source))
        except (IIIFStaticError, IIIFError) as e:
            # catch known errors and report nicely...
            logger.error("Error: " + str(e))
Ejemplo n.º 19
0
 def test07_write_html(self):
     s=IIIFStatic()
     # bad output dir
     self.assertRaises( Exception, s.write_html, '/tmp/path_does_no_exist_(i_hope)' )
     # write to good path
     tmp = tempfile.mkdtemp()
     s.identifier='abc1'
     s.write_html(tmp)
     self.assertTrue( os.path.isfile( os.path.join(tmp,'abc1.html') ) )
     # write to subdir of good path
     tmp = tempfile.mkdtemp()
     s.identifier='abc2'
     tmp2 = os.path.join(tmp,'xyz')
     s.write_html(tmp2)
     self.assertTrue( os.path.isfile( os.path.join(tmp2,'abc2.html') ) )
     # write to good path with osd
     tmp = tempfile.mkdtemp()
     s.identifier='abc3'
     s.write_html(tmp, include_osd=True)
     self.assertTrue( os.path.isfile( os.path.join(tmp,'abc3.html') ) )
     self.assertTrue( os.path.isfile( os.path.join(tmp,'osd/openseadragon.min.js') ) )
     # bad write to existing path
     tmp = tempfile.mkdtemp()
     tmp2 = os.path.join(tmp,'file')
     open(tmp2,'w').close()
     s.identifier='abc4'
     self.assertRaises( Exception, s.write_html, tmp2 )