Installation of Apache and IIPImpage Server on Windows 10

This instruction is based on the documentation How-to install IIPImageServer on your Windows computer using Apache 2.2 in 11 easy steps! As this documentation was created in 2008 the comments and readme files included in the zip files of the installation packages have to be considered.

Installation of Apache

For the installation the 32 bit version of Apache 2.4.37 was downloaded from Apache Lounge. The 32 bit version has been chosen because the IIPImage server installation was only available in 32 bit.

To start the server perform the following steps: The browser should show now a page saying "It works!". (The package "Visual C++ Redistributable for Visual Studio 2017 x86" was already installed on my Windows 10 computer. The readme file of the Apache server mentions it as a prerequisite.)

Installation von mod_fcgid

For the installation the file mod_fcgid-2.3.9-win32-VC15.zip has been downloaded from Apache Lounge.

Installation von IIPImage Server

Download the file iipsrv-1.0-win32.zip from the download page of the IIPImage server. Now a website should be shown, on which the version of the IIPImage server and the link to the project homepage is shown.

Loggings of the IIPImage server are now written into the file C:\Apache24\logs\IIPImageServer.log.

Host an image on IIPImage server

Host a test image

The test image file PalaisDuLouvre.tif with the correct format is provided on the IIPImage server page.

In the file httpd-iipimage.conf, which is located in the directory C:\Apache24\conf\extra, we made the setting FcgidInitialEnv FILESYSTEM_PREFIX "c:/Apache24/img/". This is the reason why only the image name has to be specified in the URL and no longer the whole path for the image.

When checking the correct link for the image, I observed the following errors:

Host an arbitrary image

To host your own image on the server, it must be converted into the correct image format. A TIF format is requested, that contains an image pyramide represantation of the picture. This can be realized with several tools.

The following list shows tools for that task, which I rejected: Finally I installed ImageMagick.

The command
magick convert test.jpg -define tiff:tile-geometry=256x256 -compress jpeg ptif:test.tif
converted the file test.jpg into the correct TIF format.

After copying the file test.tif into the folder C:\Apache24\img, I could see the image by opening the URL http://localhost:9090/fcgi-bin/iipsrv.fcgi?FIF=test.tif&JTL=3 in the browser.

On the search for the correct URL to display the image, the following errors were observed: Consequently I asked myself how the Universalviewer creates the correct requests to the IIPImage server. The solution is that the IIPImage server provides an image description in a JSON format with the URL http://localhost:9090/fcgi-bin/iipsrv.fcgi?IIIF=test.tif/info.json.

IIPImage Server URL Parameters

On this page I found more information about the URL parameters of the IIPImage server. The page included also the following table:

FIFImage path [FIF=/path/image.tif]
JTLReturn a tile in JPEG format with index n at resolution level r [JTL=r,n]
CVTExport the full image or a region (only JPEG format currently supported) [CVT=jpeg]
WIDRequested export image width in pixels for CVT requests [WID=w]
HEIRequested export image height in pixels for CVT requests [HEI=h]
RGNDefine a region of interest starting at relative coordinates x,y with width w and height h. All values should be in ratios 0-1.0 [RGN=x,y,w,h]
CNTContrast – multiplication of pixel values by factor [CNT=c]
ROTRotate (and flip) image by given number of degrees. Only 90, 180 and 270 supported [ROT=r]. If angle is prefixed by an exclamation mark !, the image is flipped horizontally before rotation (ex: ROT=!90). Vertical flipping can be achieved by combining horizontal flipping and 180° rotation. (iipsrv version 1.0 and later)
GAMApply gamma correction [GAM=g] (iipsrv version 1.0 and later)
SHDSimulated hill-shading for image normal data. The argument is the angle of incidence of the light source in the horizontal plane (from 12 o’clock) comma-separated with the vertical angle of incidence with 0 representing a horizontal direction and -1 vertically downwards.
CMPGenerate colormap using one of the standard colormap schemes (GREY, JET, COLD, HOT, RED, GREEN and BLUE). [CMP=s] (iipsrv version 1.0 and later)
PFLExport profile at resolution r from position x1,y1 to x2, y2. Only horizontal or vertical profiles are currently supported [PFL=r:x1,y1-x2,y2]. (iipsrv version 1.0 and later)

The following links show examples for the usage of the URL parameters. (It was helpful to clear the cache of my Firefox browser to see the correct result of the URL especially after changes made to the images that are hosted.)

Installation of Universalviewer

For the installation of the Universalviewer I used this instruction. After the installation the Universalviewer has to be started: Now the example application of the Universalviewer is available by opening the URL http://localhost:4444/examples/index.html. There different books and images can be examined.

An empty default page for the Universalviewer is available with the link http://localhost:4444/examples/uv/uv.html.

Create an own manifest.json

To create an own manifest the java project iiif-presentation-api was downloaded.

To get it running, the following steps were performed: As output the following json file has been created:
{
  "label" : "Manifest for img",
  "sequences" : [ {
    "canvases" : [ {
      "height" : 828,
      "images" : [ {
        "motivation" : "sc:painting",
        "on" : "http://localhost:9090/img/canvas/canvas-1",
        "resource" : {
          "service" : {
            "profile" : "http://iiif.io/api/image/2/level1.json",
            "@context" : "http://iiif.io/api/image/2/context.json",
            "@id" : "http://localhost:9090/img/PalaisDuLouvre.tif?"
          },
          "height" : 828,
          "width" : 4000,
          "@id" : "http://localhost:9090/img/PalaisDuLouvre.tif",
          "@type" : "dctypes:Image"
        },
        "@type" : "oa:Annotation"
      } ],
      "label" : "p-1",
      "width" : 4000,
      "@type" : "sc:Canvas",
      "@id" : "http://localhost:9090/img/canvas/canvas-1"
    }, {
      "height" : 2723,
      "images" : [ {
        "motivation" : "sc:painting",
        "on" : "http://localhost:9090/img/canvas/canvas-2",
        "resource" : {
          "service" : {
            "profile" : "http://iiif.io/api/image/2/level1.json",
            "@context" : "http://iiif.io/api/image/2/context.json",
            "@id" : "http://localhost:9090/img/test.tif?"
          },
          "height" : 2723,
          "width" : 2037,
          "@id" : "http://localhost:9090/img/test.tif",
          "@type" : "dctypes:Image"
        },
        "@type" : "oa:Annotation"
      } ],
      "label" : "p-2",
      "width" : 2037,
      "@type" : "sc:Canvas",
      "@id" : "http://localhost:9090/img/canvas/canvas-2"
    } ],
    "label" : "Current page order",
    "@type" : "sc:Sequence",
    "@id" : "http://localhost:9090/img/sequence/normal"
  } ],
  "@type" : "sc:Manifest",
  "@id" : "http://localhost:9090/img/manifest.json",
  "@context" : "http://iiif.io/api/presentation/2/context.json"
}

As there were two TIF files in the specified directory a file with two image references ( test.tif and PalaisDuLouvre.tif) has been generated. For simplicity and to make it easier to get it running, the json content was reduced to serve only the image test.tif. Additionally the following replacements were made:
{
  "label" : "Manifest for img",
  "sequences" : [ {
    "canvases" : [ {
      "height" : 2723,
      "images" : [ {
        "motivation" : "sc:painting",
        "on" : "http://localhost:9090/fcgi-bin/canvas/canvas-1",
        "resource" : {
          "service" : {
            "profile" : "http://iiif.io/api/image/2/level1.json",
            "@context" : "http://iiif.io/api/image/2/context.json",
            "@id" : "http://localhost:9090/fcgi-bin/iipsrv.fcgi?IIIF=test.tif"
          },
          "height" : 2723,
          "width" : 2037,
          "@id" : "http://localhost:9090/fcgi-bin/iipsrv.fcgi?IIIF=test.tif",
          "@type" : "dctypes:Image"
        },
        "@type" : "oa:Annotation"
      } ],
      "label" : "p-1",
      "width" : 2037,
      "@type" : "sc:Canvas",
      "@id" : "http://localhost:9090/fcgi-bin/canvas/canvas-1"
    } ],
    "label" : "Current page order",
    "@type" : "sc:Sequence",
    "@id" : "http://localhost:9090/fcgi-bin/sequence/normal"
  } ],
  "@type" : "sc:Manifest",
  "@id" : "http://localhost:9090/manifests/manifest.json",
  "@context" : "http://iiif.io/api/presentation/2/context.json"
}
This manifest.json was copied into the directory C:\Apache24\htdocs\manifests. After starting the Apache server and the Universalviewer the file test.tif could be watched by opening the URL http://localhost:4444/examples/uv/uv.html#?manifest=http://localhost:9090/manifests/manifest.json

Pitfalls on the way to host the own manifest

When I attempted to open the the URL http://localhost:4444/examples/uv/uv.html#?manifest=http://localhost:9090/manifests/manifest.json for the first time I got the error message:

Your log-in attempt did not appear to be successful. Please try again.

In the console of the browser there was the following error message visible:

Cross origin request blocked: The cross origin rule prohibits the reading of the external resource http://localhost:9090/manifests/manifest.json (Reason: CORS attack failed).

To solve this I had to follow this instruction: Then I received the error in the console:
Cross origin request blocked: The cross origin rule prohibits the reading of the external resource http://localhost:9090/fcgi-bin/iipsrv.fcgi?IIIF=test.tif/info.json (Reason: CORS attack failed).

Then it worked!

Further features

Single page and two page view

To show the icons Single page view and Two page view in the right upper corner beside the Gallery the property "viewingHint": "paged" has to be added to the sequence object.

Icons Single page view and Two page view
Picture 1: icons Single page view and Two page view

The following json snippet shows an example.

    ...
    "sequences": [ {
        "label": "Current page order",
        "@type": "sc:Sequence",
        "@id": "http://localhost:9090/fcgi-bin/sequence/normal",
        "viewingHint": "paged",
        ...
  

Further downloads

In the lower left corner of the Universalviewer page there is a download symbol that allows the Download of the current page in the JPG format.

To specify alternative Download options the property rendering has to be added to the sequence object. The following code shows an example.

    ...
    "sequences": [ {
        "label": "Current page order",
        "@type": "sc:Sequence",
        "@id": "http://localhost:9090/fcgi-bin/sequence/normal",
        "rendering": [
          {
            "@id": "http://my.pdf.link",
            "format": "application/pdf",
            "label": "Download as PDF"
          },
          {
            "@id": "http://my.txt.link",
            "format": "text/plain",
            "label": "Download raw text"
          }
        ],
        "viewingHint": "paged",
        ...
  

In this example http://my.pdf.link represents a link to a PDF download for the book and http://my.txt.link represents a link to a text file. Adding this to the sequence objects leads to the the following download view.

Alternative Downloads in the Download dialog
Picture 2: Alternative Downloads in the Download dialog

Filling the "more information" panel

On the right side of the Universalviewer page there is a panel with the title "more information", which can be expanded and collapsed.

To fill this panel with data a metadata property has to be added to the root of the manifest. This metadata property is an array containing objects with a label and a value property. The label is represented as heading and the value as paragraph below this heading. The value section may contain some HTML markup. For example links (<a href=''>) and line breaks (<br/>) are interpreted. Markups for lists (<ul>, <li>) were shown as text.

The following code snippet shows an example:

    ...
  "label": "Beitrag zur deutschen Mythologie und Sittenkunde aus dem Volksleben der Deutschen in Ungern",
  "@type": "sc:Manifest",
  "@id": "http://localhost:9090/manifests/manifest_AHB-01-AC15148377-0.json",
  "@context": "http://iiif.io/api/presentation/2/context.json",
  "metadata": [
    {
      "label": "Title",
      "value": "Beitrag zur deutschen Mythologie und Sittenkunde aus dem Volksleben der Deutschen in Ungern"
    },
    {
      "label": "Further information",
      "value": "als Aufmunterung zu größeren Sammlungen in den deutschen Gegenden Ungerns. Presburg, Wigand in Comm. 1855. 40 S. bibliografische Angaben"
    },
    {
      "label": "Author(s)",
      "value": "Unknown author"
    },
    {
      "label": "Publication date",
      "value": "1980"
    },
    {
      "label": "Permalink zum Buch",
      "value": "http://phaidra.univie.ac.at/o:912811"
    },
    {
      "label": "Permalink zur aktuellen Seite",
      "value": "http://phaidra.univie.ac.at/o:912812"
    }
  ],
  "sequences": [
    ...
  

This example renders as follows in the "more information" panel:

A filled more information panel
Picture 3: A filled "more information" panel

The license of the shown content

It is possible to link a license for the shown content at the bottom of the "more information" panel. To achieve this a license property has to be set in the root of the manifest. The value of this property is immediately rendered as link, so it is necessary to specify a valid link to a license.

The following code snippet shows an example:

    ...
    "label": "Beitrag zur deutschen Mythologie und Sittenkunde aus dem Volksleben der Deutschen in Ungern",
    "@type": "sc:Manifest",
    "@id": "http://localhost:9090/manifests/manifest_AHB-01-AC15148377-0.json",
    "@context": "http://iiif.io/api/presentation/2/context.json",
    "license": "https://creativecommons.org/licenses/by-nc/4.0/",
    "metadata":
      ...
  

Finally a picture how this license property renders in the "more information" panel.

The license section in the more information panel
Picture 4: The license section in the "more information" panel

Copyright information

It is possible to show copyright information in the "more information" panel as well as in an initial popup window when opening the resource. This popup window can be closed. To achieve this a attribution property has to be set in the root of the manifest.

The following code snippet shows an example:

    ...
    "license": "https://creativecommons.org/licenses/by-nc/4.0/",
    "attribution": "My copyright text",
    "metadata": [
      ...
  

The attribution popup and the attriution section in the more information panel
Picture 5: The attribution popup and the attriution section in the "more information" panel

Logo image

It is possible to specify a logo image that should be referenced with the resource. This might also be a logo representing the organization that is responsible for the shown content. It is recommended that the logo image is also hosted on an IIIF Server to provide operations like resizing, but it is also possible to use a link to a static image.

To integrate this small image in the "more information" panel a logo property has to be set in the root of the manifest. The following code snippet shows an example:

    ...
    "license": "Lizenz Information: CC BY-NC 2.0 AT - Creative Commons Namensnennung - Keine kommerzielle Nutzung 2.0 Österreich",
    "logo": "http://localhost:9090/favicon.png",
    "attribution": "My copyright text",
    "metadata": [
      ...
  

The logo in the more information panel
Picture 6: The logo in the "more information" panel

Table of contents

It is possible to introduce an index tab in the content panel, which represents the table of contents. Each entry is a link to the page of the book that represents the start of the chapter. The thumbnails of the book pages are still available in a separate thumbnail tab.

To achieve this a structures array has to be introduced. Each entry represents an entry in the table of contents. It consists of an unique id, the type, a label (ie. the text of the table of content entry) and the link to the canvas object that represents the first page.

The following snippet shows an example, which finally renders as shown in picture 7.

    ...
    "sequences": [
    ...
    ],
    "structures": [
        {
          "@id": "http://localhost:9090/fcgi-bin/range/r-0",
          "@type": "sc:Range",
          "label": "Buchdeckel vorne.",
          "canvases": [
            "http://localhost:9090/fcgi-bin/canvas/canvas-1"
          ]
        },
        {
          "@id": "http://localhost:9090/fcgi-bin/range/r-1",
          "@type": "sc:Range",
          "label": "Beitrag zur deutschen Mythologie und Sittenkunde. Aus dem Volksleben der Deutschen in Ungern. Mitgeteilt durch K.J. Schröer.",
          "canvases": [
            "http://localhost:9090/fcgi-bin/canvas/canvas-3"
          ]
        },
        ...
      ]
      ...
  

The table of context as index tab in the content panel
Picture 7: The table of context as index tab in the content panel

Setup German Language

In this section it is explained how to setup the German language for the example UniversalViewer application.

Adding the German Language to the settings selection

In the example UniversalViewer when the settings are opened the following locales are available:

In the lib folder of the UniversalViewer installation the following files are available:

In those files there are sections for the locales with the following content:

    "locales":[{"name":"cy-GB","label":"Cymraeg"},{"name":"en-GB","label":"English (GB)"},{"name":"fr-FR","label":"Français (FR)"},{"name":"sv-SE","label":"Svenska"}]
  

In those sections I added the following locale for German:

    {"name":"ge-GE","label":"Deutsch"}
  

Finally those sections looked like this:

    "locales":[{"name":"ge-GE","label":"Deutsch"}, {"name":"cy-GB","label":"Cymraeg"},{"name":"en-GB","label":"English (GB)"},{"name":"fr-FR","label":"Français (FR)"},{"name":"sv-SE","label":"Svenska"}]
  

After restarting the UniversalViewer and clearing the cache of the browser the German language was available in the settings dialog.

Defining the German labels

For the support of the German labels the following files must be available in the lib folder of the UniversalViewer installation:

The files I created for my installation can be downloaded here.

I also wrote a program to create those files automatically from the English base files. The program can be downloaded here and consists of the following components:

The functionality of the program is as follows:

Removing unsupported languages

To have the German language selectable in the settings for all supported languages, the German entry for the locales must be available in all *.config.json property files.

But as I can only support the English and the German language, I removed all other languages by proceeding according to the following steps:

Thumbnails are not shown

Problem description

In my initial setup the thumbnails that represent the pages of the book were not shown, although the big versions in the main view were represented correctly.

The thumbnails are not shown
Picture 8: Thumbnails are not shown.

The reason for this is, that links to the thumbnails contained two ? characters, because the universal viewer adds a parameter that looks like this: ?t=1572936590803. In total the link to retreive the thumbnail looks like this: http://localhost:9090/fcgi-bin/iipsrv.fcgi?IIIF=AHB-01-AC15148377-0/AHB-01000-AC15148377-image-001-00.tif/full/90,/0/default.jpg?t=1572936590803

Although the link would just work fine without this additional parameter, the reason for this issue as it is described in this bug report, is that the base url "is not considered an IIIF Image API url, where the iiif parameters must be stored in the path, and not in the query (where order is not important). No query parameters should be present in an url like that."

In the ongoing discussion a feature was introduced that allows to configure the UniversalViewer so that the parameter is not added to the thumbnail url. To achieve this for the property thumbsCacheInvalidation the setting of enabled must be set to false in all config files.

Solution

The solution in detail means that all config files have to be edited, so that the section

    "thumbsCacheInvalidation":{"enabled":true,"paramType":"?"}
  

is changed to

    "thumbsCacheInvalidation":{"enabled":false,"paramType":"?"}
  

As outlined in the section Setup German Language the following config files must be modified for each language: