翻译者:ginkgo
原文链接:https://iiif.io/api/content-state/0.9/

image.png

状态警告 这是一项正在进行的工作,如有更改,恕不另行通知。实现者应该意识到这个文件并不稳定。实现者可能会发现规范以不兼容的方式改变。那些有兴趣在本文档进入测试版或发布阶段之前实现该文档的人应该加入 IIIF邮件列表发现规范组,参与讨论,并关注Github 上的新问题

本文件的状态

版本: 0.9.0
最新稳定版本:
前版: 0.3
编辑

版权所有 © 2012-2021 编辑和贡献者。由 IIIF 联盟根据CC-BY许可证发布,请参阅免责声明


目录

以上这些是共享资源或资源的特定视图的示例。其他示例包括书签、引文、播放列表和与数字对象的深度链接。
IIIF 内容状态 API 的目标是提供一种标准化格式,用于共享一个或多个IIIF展示API资源的特定视图,例如收藏(Collection)、清单或清单的特定部分。
该规范还描述了如何将内容状态(例如,“要求查看器加载一个 IIIF 清单”这一内容状态)从另一个系统或应用程序传递到查看器或其他客户端应用程序,以便查看器可以向用户显示某一资源(或多个资源)中用户所需要的部分。
查看器还可以导出内容状态,例如使用户能够与其他用户共享特定视图,或将其发布为参考文献或引文。不同的 IIIF 客户端将具有不同的用户界面和受众,并且可以选择支持这些机制中的哪一种。更多详细示例可以在IIIF Cookbook 中找到。
内容状态与任何特定查看器的用户界面的状态不同。查看器状态可能是特定于客户端的,并且会涉及哪些面板处于打开状态、哪些选项被选择以及类似的用户界面详细信息。具有不同用户界面的查看器都可以实现对内容状态API 的支持。
本文档的目标读者是实现展示API 的应用程序的开发人员,不过其他社区也可能受益。

1.1.1. 与变更发现 API 的关系

通过IIIF展示API提供的资源只有在可以找到时才是可用的。变更发现API可被用于实现使这些资源得以被发现的系统,而内容状态API则可被用于在一个兼容的环境(如浏览器、注释工具或其他IIIF兼容的软件)中打开找到的资源。

1.2. 术语

该规范使用以下术语:

  • HTTP(S):HTTP 或 HTTPS URI 方案和互联网协议。
  • 本文档中的矩阵(array)JSON对象(JSON object)数字(number)字符串(string)将按照JSON规范的定义进行解释。
  • 注释(Annotation)将按照W3C Web 注释数据模型规范中的定义进行解释。

本文档中的关键词 必须(must/required/shall)、禁止(must not/shall not)、推荐(should/recommended)、不推荐(should not)、 可选/可以选择(may/optional)按照RFC 2119中的描述进行解释。

2. 内容状态

内容状态是一种 JSON-LD 数据结构,它使用IIIF展示APIW3C Web 注释数据模型规范中描述的模型。该数据结构是对资源或资源的部分的描述。它必须允许客户端加载所需的资源,并将资源的特定部分呈现给用户。状态可能非常简单:例如,指向清单的链接。更复杂的内容状态将提供有关预期目标的更多详细信息。以下是传入的内容状态可能在兼容客户端中产生的所有行为:

  • 加载清单 M
  • 加载清单 M,导航到画布C,并放大到 xywh=X,Y,W,H定义的区域
  • 加载清单 M,导航以选择范围 R,并在时间 t=T时开始在范围 R内播放基于时间的画布 C

这些意图可以正式表达为针对预期资源或资源的部分的注释。此内容状态注释必须轻松传递到web应用程序中,例如,作为查询字符串参数或 HTML的data-属性。

2.1. 内容状态的注释模型

注释的目标是 IIIF 资源,使用与 IIIF 查看器可能遇到的任何其他针对IIIF的注释完全相同的模式。
目标可以是展示API 描述的任何资源,例如:

  • 清单
  • 范围(Range)
  • 画布(Canvas)
  • 画布的空间或时间片段
  • 画布上的空间或时间点

注释必须包含有关可引用资源的足够信息以在上下文中显示内容。例如,画布通常缺乏足够的信息让查看器显示预期的视图;清单和画布是需要声明的一部分,以便客户端可以首先加载该清单,然后在其中找到画布。

2.2 注释形式

任何注释都可能有一个或多个动机(motivation),这些动机描述了创建的原因。例如,bookmarking。
一个内容状态注释总是有动机contentState。这个动机不是由W3C Web 注释数据模型或 IIIF展示API 定义的,当内容状态注释的目标本身是一个注释时,选择这个动机是为了避免潜在的歧义。内容状态注释还可以有其它动机如bookmarking,identifying等由W3C Web 注释模型定义的动机,但其特定的动机contentState产生兼容软件所需的行为。
内容状态注释可以以多种形式提供,接下来对其进行描述。
推荐发布者以以下形式之一提供内容状态注释。推荐客户端能够接受和处理所有这些形式的内容状态。

2.2.1 完整注释

内容状态可以选择以JSON-LD形式提供,作为一个完整的带有动机contentState的注释,如下例所示:

  1. {
  2. "@context": "http://iiif.io/api/presentation/3/context.json",
  3. "id": "https://example.org/Annotation-server/bookmarks/b1",
  4. "type": "Annotation",
  5. "motivation": ["contentState"],
  6. "target": {
  7. "id": "https://example.org/iiif/item1/manifest",
  8. "type": "Manifest"
  9. }
  10. }

在这种情况下,注释的目标是一个完整的 IIIF 资源(这里是一个清单),但在更复杂的情况下,目标可能是 IIIF 资源的一部分。

2.2.2 注释URI

内容状态可以选择以字符串形式提供,其值是一个带有动机contentState的注释URI,客户端必须引用和处理。对于上面 2.2.1 中的示例,这将是 URI https://example.org/Annotation-server/bookmarks/b1。来自该 URI 的响应将是上面的 JSON。

2.2.3 目标主体

内容状态可以选择以 JSON-LD形式提供,作为具有动机contentState的隐含注释的target属性的取值。对于 2.2.1 中的示例,这将是:

  1. {
  2. "id": "https://example.org/iiif/item1/manifest",
  3. "type": "Manifest"
  4. }

这种形式更适合于紧凑性很重要的场景,例如查询字符串参数,它将与第 2.3 节中描述的编码结合。

2.2.4 目标URI

内容状态可以选择以字符串形式提供,其内容仅包括target属性中的id(即可引用的 URI)。这是最简单的形式,仅包含资源的 URI。对于 2.2.1 中的示例,这将是 URI https://example.org/iiif/item1/manifest。客户端只需加载此清单并显示它。
示例 2.2.2 和 2.2.4 都是 URI。由客户端来识别 2.2.4 是一个清单,而 2.2.2 是一个指向清单的内容状态注释。客户端必须检查type属性以确定引用的资源是什么。如果type的取值是Annotation,客户端还必须查看motivation属性以确定注释是否是contentState。如果motivation的取值不是contentState,但在应该是内容状态的地方遇到了注释,则客户端必须假定注释本身是需要的 IIIF 内容。

2.2.5 简单 URI 的局限性

虽然可以满足资源共享和客户端应用程序初始化的许多要求,但 2.2.4 中的这种形式的URI无法表达属于某一IIIF资源某一部分的内容状态,如某一画布的某一区域,又如未引用自身的某一画布URI。因此需要另外一种形式的URI来满足这些目的。

  1. {
  2. "@context": "http://iiif.io/api/presentation/3/context.json",
  3. "id": "https://example.org/import/1",
  4. "type": "Annotation",
  5. "motivation": ["contentState"],
  6. "target": {
  7. "id": "https://example.org/object1/canvas7#xywh=1000,2000,1000,2000",
  8. "type": "Canvas",
  9. "partOf": [{
  10. "id": "https://example.org/object1/manifest",
  11. "type": "Manifest"
  12. }]
  13. }
  14. }

不能仅通过画布URI 或 清单URI 来传达此描述;它需要内容状态注释提供的结构。它可以简化为目标主体形式,但不能进一步:

  1. {
  2. "id": "https://example.org/object1/canvas7#xywh=1000,2000,1000,2000",
  3. "type": "Canvas",
  4. "partOf": [{
  5. "id": "https://example.org/object1/manifest",
  6. "type": "Manifest"
  7. }]
  8. }

2.3 协议和编码要求

可以通过多种方式在 IIIF 兼容系统之间交换第 2.2 节中所示的内容状态数据。出于互操作性的要求,仅就模型达成一致是不够的。本节定义了用于传输此数据的协议,以便实现软件可以在不了解其他参与软件的特定知识的情况下接收或传递内容状态。这些协议利用了现代浏览器广泛支持的功能:

  • 在 HTTP GET 请求中将内容状态作为查询字符串参数传递
  • 在 HTTP POST 请求中将内容状态作为参数传递
  • Paste事件做出反应,其中粘贴的数据是内容状态的 URI
  • Paste事件做出反应,其中粘贴的数据是完整的内容状态
  • 使用拖放 API公开和接受内容状态
  • 通过FileReader界面从客户端机器上传内容状态
  • 通过 HTML5中的data-*属性初始化客户端

这些协议的示例在第 3 节初始化机制中给出。

2.3.1 内容状态编码

内容状态将通过本规范中描述的各种方法从客户端传递到服务器,以及从服务器传递到客户端。内容状态将包含使用 JSON 语法的字符,并且可能包含来自任何语言的字符串。内容状态内的 IIIF 和其他资源的标识符可以是国际化资源标识符 (IRI),如RFC 3987 中所定义的那样。出于这些原因,必须使用具有以下性质的编码对内容状态进行编码

  • 易于在 web 浏览器和服务器上实现解码和编码
  • 将安全地编码来自 JavaScript 的任何 UTF-16 字符串
  • 不受双重编码的影响——也就是说,一旦编码,任何请求或响应部分的任何进一步可能的编码都不会改变已经编码的内容状态。

出于这些原因,本规范定义了一种两步编码,它首先使用web 浏览器中可用的encodeURI函数,然后使用base64url编码(见RFC4648)删除填充字符。初始的 encodeURI 步骤允许 JavaScript 中的任何 UTF-16 字符串,以便其在 web 浏览器中被安全地编码为 base64url。删除填充的最后一步是删除“=”字符,该字符可能会作为 URL 的一部分进行百分比编码。
请注意,“base64url”与“base64”的编码不同。
编码:

  • 编码为由encodeURI描述的 UTF-8 字符串
  • 将生成的 UTF-8 字符串编码为base64url
  • 从末尾删除任何“=”填充字符

解码:

  • 将任何丢失的“=”填充字符恢复到字符串的末尾
  • 将生成的字符串从 base64url 解码为 UTF-8 字符串
  • 按照decodeURI 的描述解码生成的 UTF-8 字符串

这些操作的代码示例在下一节中给出。
任何 JSON-LD 形式的内容状态(而非简单的 URI 字符串),当在 HTML 文档中内联声明或作为 HTTP 请求参数(例如,GET 或 POST)传递时,必须以这种方式编码,并且客户端必须以这种形式接受它。
简单的 URI 形式可以选择用纯字符串、URI 编码的纯字符串或内容状态编码的字符串,客户端必须接受这些形式并区分它们。
当以 2.2 中给出的完整形式作为内联编码 JSON-LD 发布时,内容状态注释可以选择省略id和@context属性。
当在服务器上发布以供客户端通过 HTTP 获取时,与客户端获取清单或收藏的方式相同,内容状态必须是符合IIIF 展示 API 的有效的 JSON-LD 文档,并按照下面第 5 节中的描述提供服务。它们不推荐被编码,但可以选择具有适用于 JSON 内容的其他编码,例如Content-Encoding: gzip以减小响应大小。

2.3.2 内容状态编码示例

  1. // JavaScript
  2. function encodeContentState(plainContentState) {
  3. let uriEncoded = encodeURI(plainContentState); // using built in function
  4. let base64 = btoa(uriEncoded); // using built in function
  5. let base64url = base64.replace(/\+/g, "-").replace(/\//g, "_");
  6. let base64urlNoPadding = base64url.replace(/=/g, "");
  7. return base64urlNoPadding;
  8. }
  9. function decodeContentState(encodedContentState) {
  10. let base64url = restorePadding(encodedContentState);
  11. let base64 = base64url.replace(/-/g, '+').replace(/_/g, '/');
  12. let base64Decoded = atob(base64); // using built in function
  13. let uriDecoded = decodeURI(base64Decoded); // using built in function
  14. return uriDecoded;
  15. }
  16. function restorePadding(s) {
  17. let pad = s.length % 4;
  18. if (pad) {
  19. if (pad === 1) {
  20. throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding');
  21. }
  22. s += new Array(5 - pad).join('=');
  23. }
  24. return s;
  25. }
  1. # Python
  2. import base64
  3. import urllib
  4. def encode_content_state(plain_content_state):
  5. uri_encoded = urllib.parse.quote(plain_content_state, safe=',/?:@&=+$#') # equivalent of encodeURI
  6. utf8_encoded = uri_encoded.encode("UTF-8")
  7. base64url = base64.urlsafe_b64encode(utf8_encoded)
  8. utf8_decoded = base64url.decode("UTF-8")
  9. base64url_no_padding = utf8_decoded.replace("=", "")
  10. return base64url_no_padding
  11. def decode_content_state(encoded_content_state):
  12. padded_content_state = restore_padding(encoded_content_state)
  13. base64url_decoded = base64.urlsafe_b64decode(padded_content_state)
  14. utf8_decoded = base64url_decoded.decode("UTF-8")
  15. uri_decoded = urllib.parse.unquote(utf8_decoded)
  16. return uri_decoded
  17. def restore_padding(s):
  18. pad = len(s) % 4
  19. padding = ""
  20. if pad:
  21. if pad == 1:
  22. raise Exception("InvalidLengthError: Input base64url string is the wrong length to determine padding")
  23. padding = "=" * (5 - pad)
  24. return s + padding

给定以下内容状态注释:

  1. {
  2. "id": "https://example.org/object1/canvas7#xywh=1000,2000,1000,2000",
  3. "type": "Canvas",
  4. "partOf": [{
  5. "id": "https://example.org/object1/manifest",
  6. "type": "Manifest"
  7. }]
  8. }

可以压缩 JSON 以删除不必要的空格:

  1. {"id":"https://example.org/object1/canvas7#xywh=1000,2000,1000,2000","type":"Canvas","partOf":[{"id":"https://example.org/object1/manifest","type":"Manifest"}]}

然后对压缩形式进行编码(此示例在 Python 中):

  1. >>> encode_content_state(condensed)
  2. 'JTdCJTIyaWQlMjI6JTIyaHR0cHM6Ly9leGFtcGxlLm9yZy9vYmplY3QxL2NhbnZhczcjeHl3aD0xMDAwLDIwMDAsMTAwMCwyMDAwJTIyLCUyMnR5cGUlMjI6JTIyQ2FudmFzJTIyLCUyMnBhcnRPZiUyMjolNUIlN0IlMjJpZCUyMjolMjJodHRwczovL2V4YW1wbGUub3JnL29iamVjdDEvbWFuaWZlc3QlMjIsJTIydHlwZSUyMjolMjJNYW5pZmVzdCUyMiU3RCU1RCU3RA'

字符串“JTdC…”现在是 URI 安全的内容状态编码形式,适用于在 web 应用程序之间传递。
并非所有向客户端提供内容状态的用例都需要编码,因为并非所有用例都容易受到 HTTP 传输问题的影响。例如,用户界面可以允许用户直接粘贴甚至键入内容状态 JSON-LD。第 3 节给出了示例。在这些场景中,客户端也应该接受未编码的 JSON。

2.3.3 协议

取值通过请求参数或 HTML 元素上的属性传递到 web 应用程序。
如果客户端能够从 HTTP GET 或 POST 请求参数的取值中读取内容状态,则它必须在名为iiif-content的请求参数中查找内容状态。
如果客户端能够从实例化客户端的 HTML 元素上的属性值读取内容状态,则推荐它在名为data-iiif-content的属性中查找内容状态。
内容状态注释可以作为 HTML 文档的一部分内联发布,如前面的data-iiif-content示例,或者作为查询字符串参数构成HTML锚标签的href属性的一部分。
如果内容状态是一个简单的 URI,则客户端必须在该 URI 处加载资源并对其进行处理。该 URI 处的资源必须是如 2.2.2 中所示的完整注释,或如 2.2.4中所示的IIIF 资源。也就是说,引用的响应必须是JSON-LD,并推荐type的取值从Annotation,Collection,Manifest,Canvas和Range中选择。响应必须使用 UTF-8 编码。
如果type属性引用的资源是Canvas或Range,则该资源必须包含在其中能够找到画布或范围的清单 URI,并使用如2.3.2中所示的partOf属性。
如果内容状态是 JSON-LD形式的,则客户端必须检查type属性以决定该值是完整的内容状态注释(由额外的contentState动机指示)(如示例 2.2.1 中所示),还是一个隐含的内容状态注释的target属性的取值(示例 2.2.3)。

3. 初始化

本规范提供了 IIIF 兼容软件可以用来公开、共享和传输内容状态描述的机制,但没有指定 IIIF 兼容软件本身可以采用什么形式(例如,网页、JavaScript web 应用程序、原生移动应用程序、桌面应用程序,或显示kiosk硬件)。请参阅IIIF Cookbook以获取下面列出的模式的更多示例。

3.1. 初始化机制(协议)

可以选择使用以下机制使数据结构对客户端可用。其他机制也是可能的,但超出了规范的范围。

3.1.1 链接:HTTP GET(查询字符串)参数

如果目的是相链接的客户端需要加载整个 IIIF 资源而不关注任何特定部分,推荐使用最简单的内容状态形式:

  1. <a href="https://example.org/viewer?iiif-content=http://dams.llgc.org.uk/iiif/2.0/4389767/manifest.json">Link to Viewer</a>

在这种情况下,客户端https://example.org/viewer将加载提供的资源,确定它是一个清单(而不是一个收藏),并进行相应的处理。
当打算在资源的特定部分初始化查看器时,有更多选项可用。
在以下示例中,每次都使用相同的注释。如 JSON:

  1. {
  2. "type": "Annotation",
  3. "motivation": ["contentState"],
  4. "target": {
  5. "id": "http://dams.llgc.org.uk/iiif/2.0/4389767/canvas/4389772.json",
  6. "type": "Canvas",
  7. "partOf": [
  8. {
  9. "id": "http://dams.llgc.org.uk/iiif/2.0/4389767/manifest.json",
  10. "type": "Manifest"
  11. }
  12. ]
  13. }
  14. }

这种用法的一个例子是从搜索结果到数字化书籍特定页面的链接,或特定页面(如画布)的存储书签。
如果没有所需的编码,到查看器的(无效)链接将如下所示:

  1. <a href='https://example.org/viewer?iiif-content={"type":"Annotation","motivation":"contentState","target":{"id":"http://dams.llgc.org.uk/iiif/2.0/4389767/canvas/4389772.json","type":"Canvas","partOf":[{"id":"http://dams.llgc.org.uk/iiif/2.0/4389767/manifest.json","type":"Manifest"}]}}'>INVALID, unencoded link to Viewer</a>

但是,使用 JSON-LD则必须按照第 2.3 节中的描述进行编码:

  1. <a href="https://example.org/viewer?iiif-content=JTdCJTIydHlwZSUyMjolMjJBbm5vdGF0aW9uJTIyLCUyMm1vdGl2YXRpb24lMjI6JTIyY29udGVudFN0YXRlJTIyLCUyMnRhcmdldCUyMjolN0IlMjJpZCUyMjolMjJodHRwOi8vZGFtcy5sbGdjLm9yZy51ay9paWlmLzIuMC80Mzg5NzY3L2NhbnZhcy80Mzg5NzcyLmpzb24lMjIsJTIydHlwZSUyMjolMjJDYW52YXMlMjIsJTIycGFydE9mJTIyOiU1QiU3QiUyMmlkJTIyOiUyMmh0dHA6Ly9kYW1zLmxsZ2Mub3JnLnVrL2lpaWYvMi4wLzQzODk3NjcvbWFuaWZlc3QuanNvbiUyMiwlMjJ0eXBlJTIyOiUyMk1hbmlmZXN0JTIyJTdEJTVEJTdEJTdE">Link to Viewer</a>

内容状态可以作为一个带有contentState动机的隐含注释的target属性传递,即:

  1. {
  2. "id": "http://dams.llgc.org.uk/iiif/2.0/4389767/canvas/4389772.json",
  3. "type": "Canvas",
  4. "partOf": [
  5. {
  6. "id": "http://dams.llgc.org.uk/iiif/2.0/4389767/manifest.json",
  7. "type": "Manifest"
  8. }
  9. ]
  10. }

这会导致更紧凑的未编码形式,这将是:

  1. <a href='https://example.org/viewer?iiif-content={"id":"http://dams.llgc.org.uk/iiif/2.0/4389767/canvas/4389772.json","type":"Canvas","partOf":[{"id":"http://dams.llgc.org.uk/iiif/2.0/4389767/manifest.json","type":"Manifest"}]}'>Link to Viewer</a>

但是,使用 JSON-LD仍然必须按照第 2.3 节所示进行编码:

  1. <a href="https://example.org/viewer?iiif-content=aHR0cHM6Ly9leGFtcGxlLm9yZy92aWV3ZXI_aWlpZi1jb250ZW50PSU3QiUyMmlkJTIyOiUyMmh0dHA6Ly9kYW1zLmxsZ2Mub3JnLnVrL2lpaWYvMi4wLzQzODk3NjcvY2FudmFzLzQzODk3NzIuanNvbiUyMiwlMjJ0eXBlJTIyOiUyMkNhbnZhcyUyMiwlMjJwYXJ0T2YlMjI6JTVCJTdCJTIyaWQlMjI6JTIyaHR0cDovL2RhbXMubGxnYy5vcmcudWsvaWlpZi8yLjAvNDM4OTc2Ny9tYW5pZmVzdC5qc29uJTIyLCUyMnR5cGUlMjI6JTIyTWFuaWZlc3QlMjIlN0QlNUQlN0Q">Link to Viewer</a>

3.1.2 HTTP POST(表单)参数

相同格式的相同数据结构可以改为在 HTTP POST 中传递给服务器。这适用于服务器端 web 应用程序,例如获取引文的网页或者在服务器上初始化的视图。它不适合初始化独立的 JavaScript 应用程序,因为 POST 数据通常不可用。
数据应与Content-Type标头的取值application/x-www-form-urlencoded一起发送,此外还需一起发送参数名iiif-content,如下例所示。这允许从简单的 HTML 表单提交内容状态(例如,粘贴引用)。Curl 默认使用 form-urlencoded 进行 POST:

  1. curl -d 'iiif-content=aHR0cHM6Ly9leGFtcGxlLm9yZy92aWV3ZXI_aWlpZi1jb250ZW50PSU3QiUyMmlkJTIyOiUyMmh0dHA6Ly9kYW1zLmxsZ2Mub3JnLnVrL2lpaWYvMi4wLzQzODk3NjcvY2FudmFzLzQzODk3NzIuanNvbiUyMiwlMjJ0eXBlJTIyOiUyMkNhbnZhcyUyMiwlMjJwYXJ0T2YlMjI6JTVCJTdCJTIyaWQlMjI6JTIyaHR0cDovL2RhbXMubGxnYy5vcmcudWsvaWlpZi8yLjAvNDM4OTc2Ny9tYW5pZmVzdC5qc29uJTIyLCUyMnR5cGUlMjI6JTIyTWFuaWZlc3QlMjIlN0QlNUQlN0Q' -X POST https://example.org/citation-renderer

在这个例子中,服务器https://example.org/citation-renderer应准备好以与上述形式及其变种相同的形式来处理内容状态。

3.1.3 接受内容状态作为粘贴操作

客户端允许将内容状态 URI 或数据粘贴到其 UI 的一部分中(例如,从“加载…”选项公开一个textarea元素供用户手动粘贴)。客户端还可以通过从剪贴板读取来透明地接受粘贴操作:

  1. <script>
  2. document.addEventListener('paste', event => {
  3. const text = event.clipboardData.getData('text/plain');
  4. Annotation = JSON.parse(text);
  5. //... process Annotation
  6. });
  7. </script>


在这种情况下,用户可以将内容状态直接粘贴到视图中。如果支持这种情况,推荐客户端接受未编码的 JSON 以及内容状态编码的 JSON,并且推荐直接接受资源 URI,例如清单 URI。
请参阅下面的第 3.2 节了解从一个查看器到另一个查看器的数据导出方法,包括复制到剪贴板模式、粘贴操作的自然配对。

3.1.4 拖放

在这种情况下,一个客户端提供了一个可拖动元素:

  1. <img src="http://iiif.io/img/logo-iiif-34x30.png" draggable="true" ondragstart="drag(event)" />
  2. <script>
  3. function getContentStateAnnotation(){
  4. // return a stringified representation of the required content state
  5. }
  6. function drag(ev) {
  7. var json = getContentStateAnnotation();
  8. ev.dataTransfer.setData("text/plain", json);
  9. }
  10. </script>

另一个客户端提供了一个能够接收drop事件的元素:

  1. <div id="dropbox" ondrop="drop(event)" ondragover="allowDrop(event)" ondragexit="deselect(event)">
  2. <!-- this could be the viewport -->
  3. </div>
  4. <script>
  5. function drop(ev) {
  6. ev.preventDefault();
  7. var dropDiv = document.getElementById("dropbox");
  8. var json = ev.dataTransfer.getData("text/plain");
  9. // process the Annotation
  10. }
  11. function allowDrop(ev) {
  12. ev.preventDefault();
  13. // indicate visually that the drop area is ready to receive a drop
  14. }
  15. function deselect(ev) {
  16. // remove visual indication that drop is possible
  17. }
  18. </script>

这种技术也可以在同一个客户端中使用,将内容状态从一个部分拖到另一个部分。
setData方法和getData方法的第一个参数是内容类型,并且为保证在本规范范围之内的最大互操作性,它必须是“text/plain”。应用程序可以为其自己的自定义行为断言多种附加内容类型,例如从应用程序拖动到桌面并另存为文件,但这超出了当前内容状态 API 的范围。在上面的例子中,拖放操作的内容可以是一个普通的 URI,或者 JSON-LD。如果是 JSON-LD,推荐接收数据的客户端(通过调用getData方法)接受未编码和编码形式的数据。

3.1.5 上传文件

JavaScript 客户端可以通过FileReader界面从客户端机器接受内容状态:

  1. <form>
  2. <input type="file" id="selectFiles" value="Import" /><br />
  3. <button id="import" onclick="return loadAnnotation()">Import</button>
  4. </form>
  5. <script>
  6. function loadAnnotation() {
  7. var files = document.getElementById('selectFiles').files;
  8. var fr = new FileReader();
  9. fr.onload = function(e) {
  10. loadJson(e.target.result);
  11. }
  12. fr.readAsText(files.item(0));
  13. return false;
  14. }
  15. function loadJson(json) {
  16. var contentState = JSON.parse(json);
  17. // process contentState
  18. }
  19. </script>

3.1.6 通过引用加载

这是 3.1.1 的变体,参数值为 URI 而不是内容本身。

  1. <a href="https://example.org/viewer?iiif-content=https://publisher.org/fragment123.json">Link to Viewer</a>

同样的规则仍然适用;查看器必须引用并处理该 URI 处的注释。

3.1.7 常用初始化参数

如果 IIIF 客户端可以通过自定义 HTML 属性接受内容状态,那么出于此目的推荐它使用data-iiif-content属性,以帮助使用该客户端的页面开发人员了解该属性的用途。推荐接受内容状态的查看器以 GET 参数部分中描述的任何形式处理注释。

  1. <p>Loading a whole manifest</p>
  2. <div
  3. id="iiif-viewer"
  4. data-iiif-content="http://dams.llgc.org.uk/iiif/2.0/4389767/manifest.json">
  5. </div>
  6. <p>Loading a manifest to show a particular Canvas</p>
  7. <div
  8. id="iiif-viewer"
  9. data-iiif-content="aHR0cHM6Ly9leGFtcGxlLm9yZy92aWV3ZXI_aWlpZi1jb250ZW50PSU3QiUyMmlkJTIyOiUyMmh0dHA6Ly9kYW1zLmxsZ2Mub3JnLnVrL2lpaWYvMi4wLzQzODk3NjcvY2FudmFzLzQzODk3NzIuanNvbiUyMiwlMjJ0eXBlJTIyOiUyMkNhbnZhcyUyMiwlMjJwYXJ0T2YlMjI6JTVCJTdCJTIyaWQlMjI6JTIyaHR0cDovL2RhbXMubGxnYy5vcmcudWsvaWlpZi8yLjAvNDM4OTc2Ny9tYW5pZmVzdC5qc29uJTIyLCUyMnR5cGUlMjI6JTIyTWFuaWZlc3QlMjIlN0QlNUQlN0Q">
  10. </div>

3.2 导出状态

除了在示例 3.1.4 中填充拖放操作之外,还有其他方法可以让客户端导出状态。虽然互操作性问题要求本规范描述客户端可以接受状态的方式,但内容状态被复制到用户剪贴板的方式不在本规范范围之内,可以参考 Cookbook 中的介绍。这些包括:

  • 复制到剪贴板
  • 下载文件
  • 显示以便复制
  • 发送到外部(如引用接受服务)

    4. 内容状态示例

    以下示例演示了使用现有的IIIF展示API 和 W3C Web 注释数据模型来描述资源的某些部分。任何可以在展示模型中表达的 IIIF 资源都可以用于内容状态。在每种情况下都使用了完整形式的注释(就好像它在id属性中给定的 URI 处可用)。更多的例子可以在IIIF Cookbook 中找到。
    发布者应该努力提供最简单的 JSON-LD 展示,而不是假设任何客户端都可以处理任意复杂的内容状态。

    4.1. 清单中画布的区域

    1. {
    2. "@context": "http://iiif.io/api/presentation/3/context.json",
    3. "id": "https://example.org/import/1",
    4. "type": "Annotation",
    5. "motivation": ["contentState"],
    6. "target": {
    7. "id": "https://example.org/object1/canvas7#xywh=1000,2000,1000,2000",
    8. "type": "Canvas",
    9. "partOf": [{
    10. "id": "https://example.org/object1/manifest",
    11. "type": "Manifest"
    12. }]
    13. }
    14. }
    当由查看器处理时,用户应该看到参数中1000,2000,1000,2000给出的画布上突出显示的矩形id;查看器加载partOf属性中链接到的清单并导航到该画布,然后用该矩形填充视口或以其他方式引起注意。

    4.2. 在录音中的某个点开始播放

    1. {
    2. "@context": "http://iiif.io/api/presentation/3/context.json",
    3. "id": "https://example.org/import/2",
    4. "type": "Annotation",
    5. "motivation": ["contentState"],
    6. "target": {
    7. "type": "SpecificResource",
    8. "source": {
    9. "id": "https://example.org/iiif/id1/canvas1",
    10. "type": "Canvas",
    11. "partOf": [{
    12. "id": "https://example.org/iiif/id1/manifest",
    13. "type": "Manifest"
    14. }]
    15. },
    16. "selector": {
    17. "type": "PointSelector",
    18. "t": 14.5
    19. }
    20. }
    21. }
    这个例子应该让查看器打开清单 https://example.org/iiif/id1/manifest,导航到画布 https://example.org/iiif/id1/canvas1,并在 14.5 秒开始播放到该画布。

    4.3. 比较视图的多个目标

    1. {
    2. "@context": "http://iiif.io/api/presentation/3/context.json",
    3. "id": "https://example.org/import/3",
    4. "type": "Annotation",
    5. "motivation": "contentState",
    6. "target": [
    7. {
    8. "id": "https://example.org/iiif/item1/canvas37",
    9. "type": "Canvas",
    10. "partOf": [
    11. {
    12. "id": "https://example.org/iiif/item1/manifest",
    13. "type": "Manifest"
    14. }
    15. ]
    16. },
    17. {
    18. "id": "https://example.org/iiif/item2/canvas99",
    19. "type": "Canvas",
    20. "partOf": [
    21. {
    22. "id": "https://example.org/iiif/item2/manifest",
    23. "type": "Manifest"
    24. }
    25. ]
    26. }
    27. ]
    28. }
    此处,查看器应该一次打开两个清单(如果它能够这样查看)。

    4.4. 搜索结果

    以下示例使用内容状态的紧凑查询字符串形式来演示链接到特定查看器的 HTML 搜索结果可能是什么样子。
    首先,以无效的、未编码的形式显示注释:
    1. <h2>Results for "cats"</h2>
    2. <ol>
    3. <li>
    4. <h3><a href='viewer.html?iiif-content={"id":"https://example.org/alice/canvas77#xywh=1000,2000,1000,2000","type":"Canvas","partOf":[{"id":"https://example.org/alice/manifest","type":"Manifest"}]}'>Alice in Wonderland</a></h3>
    5. <p>...she has often seen a <b>cat</b> without a grin but never a grin without a <b>cat</b></p>
    6. </li>
    7. <!-- ... more results -->
    8. </ol>
    …然后以编码形式:
    1. <h2>Results for "cats"</h2>
    2. <ol>
    3. <li>
    4. <h3><a href="viewer.html?iiif-content=JTdCJTIyaWQlMjI6JTIyaHR0cHM6Ly9leGFtcGxlLm9yZy9hbGljZS9jYW52YXM3NyN4eXdoPTEwMDAsMjAwMCwxMDAwLDIwMDAlMjIsJTIydHlwZSUyMjolMjJDYW52YXMlMjIsJTIycGFydE9mJTIyOiU1QiU3QiUyMmlkJTIyOiUyMmh0dHBzOi8vZXhhbXBsZS5vcmcvYWxpY2UvbWFuaWZlc3QlMjIsJTIydHlwZSUyMjolMjJNYW5pZmVzdCUyMiU3RCU1RCU3RA">Alice in Wonderland</a></h3>
    5. <p>...she has often seen a <b>cat</b> without a grin but never a grin without a <b>cat</b></p>
    6. </li>
    7. <!-- ... more results -->
    8. </ol>

    5. HTTP 请求和响应

    本节介绍当 API 用作 HTTP 响应的 JSON-LD 主体时推荐的请求和响应交互。它不适用于内联内容状态,这些状态按照第 2.3 节中的描述进行编码,并通过上述其他机制进行传输。本节遵循展示 API第 6 节中给出的规范。

    5.1. 请求

    对内容状态的 HTTP 请求与对 展示 API 资源的 HTTP 请求相同。与IIIF 图像 API请求或其他参数化服务不同,不能假定 展示 API 资源的 URI 遵循任何特定模式。通过相同机制获取内容状态的 URI 和展示 API 资源(例如清单和收藏)的 URI 的客户端必须检查响应以确定它是展示 API 资源还是引用展示 API 一部分的内容状态资源。

    5.2. 响应

    如上所述,内容状态作为 HTTP 响应的格式是 JSON。当 URI 被引用时,所有具有 HTTP(S) URI 的资源都提供其描述是一种很好的做法。如果资源在响应中被引用,而不是被嵌入,那么它必须能够被解引用。
    如果服务器收到一个带有Accept 标头的请求,推荐它按照内容协商的规则进行响应。请注意,Accept请求标头中提供的内容类型可以选择包含参数,例如profile或charset。
    如果请求不包含Accept 标头 ,则响应的 HTTP Content-Type 标头推荐取值为application/ld+json(JSON-LD) ,而profile参数则作为上下文文档给出:http://iiif.io/api/presentation/3/context.json。
    1. Content-Type: application/ld+json;profile="http://iiif.io/api/presentation/3/context.json"
    如果Content-Type 标头 的 application/ld+json因服务器配置细节不能产生,那么推荐将Content-Type 标头改为application/json(常规JSON),而无需profile参数。
    1. Content-Type: application/json
    HTTP 服务器必须遵循CORS 要求才能使基于浏览器的客户端能够检索描述。Apache HTTP Server 实现说明中提供了启用 CORS 和条件 Content-Type 标头的方法。

    附录

    A. 致谢

    非常感谢IIIF 社区成员的持续参与、创新想法和反馈。
    该版本由IIIF 发现技术规范小组完成,该小组由 Antoine Isaac(欧洲数字文化遗产平台)、Matthew McGrattan(Digirati)和 Rob Sanderson(耶鲁大学)担任主席。IIIF 社区感谢他们的领导,感谢小组成员的不懈努力。

    B. 更改日志

    | 日期 | 描述 | | —- | —- | | 2021-06-21 | 版本 0.9(未命名) | | 2020-11-22 | 版本 0.3(未命名) | | 2019-02-04 | 版本 0.2(未命名) | | 2018-10-31 | 版本 0.1(未命名) |