1"use strict";(globalThis.webpackChunkopensource_contentauth_org=globalThis.webpackChunkopensource_contentauth_org||[]).push([[6975],{209:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>v,contentTitle:()=>j,default:()=>y,frontMatter:()=>x,metadata:()=>r,toc:()=>w});const r=JSON.parse('{"id":"tasks/get-resources","title":"Getting resources from a manifest","description":"Manifest data can include binary resources such as thumbnail and icon images which are referenced by JUMBF URIs in manifest data.","source":"@site/docs/tasks/get-resources.mdx","sourceDirName":"tasks","slug":"/tasks/get-resources","permalink":"/docs/tasks/get-resources","draft":false,"unlisted":false,"editUrl":"https://github.com/contentauth/opensource.contentauth.org/edit/main/docs/tasks/get-resources.mdx","tags":[],"version":"current","frontMatter":{"id":"get-resources","title":"Getting resources from a manifest"},"sidebar":"docs","previous":{"title":"Reading manifest data","permalink":"/docs/tasks/read"},"next":{"title":"Using builder intents","permalink":"/docs/tasks/intents"}}');var a=t(4848),s=t(8453),i=t(4865),o=t(9365);function c(e){const n={code:"code",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.p,{children:"The example below shows how to get resources from manifest data using the Python library."}),"\n",(0,a.jsxs)(n.p,{children:["To retrieve binary resources such as thumbnails from the manifest data, use the ",(0,a.jsx)(n.code,{children:"resource_to_stream"})," method with the associated ",(0,a.jsx)(n.code,{children:"identifier"})," field value as the URI."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-py",children:'import json\nfrom c2pa import Context, Reader\n\ntry:\n with Context() as ctx:\n with Reader("path/to/media_file.jpg", context=ctx) as reader:\n manifest_store = json.loads(reader.json())\n active_manifest_label = manifest_store.get("active_manifest")\n if active_manifest_label:\n manifest = manifest_store["manifests"][active_manifest_label]\n\n # Get the URI to the manifest\'s thumbnail and write it to a file.\n uri = manifest["thumbnail"]["identifier"]\n with open("thumbnail_v2.jpg", "w+b") as thumb_file:\n reader.resource_to_stream(uri, thumb_file)\n\nexcept Exception as err:\n print(err)\n'})})]})}function u(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(c,{...e})}):c(e)}function l(e){const n={code:"code",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.p,{children:"This is how to get resources from a manifest using C++."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-cpp",children:'#include <iostream>\n#include <fstream>\n#include <string>\n#include "c2pa.hpp"\n#include <nlohmann/json.hpp>\n\n// this example uses nlohmann json for parsing the manifest\nusing json = nlohmann::json;\nusing namespace std;\nnamespace fs = std::filesystem;\nusing namespace c2pa;\n\nint main()\n{\n fs::path output_path = current_dir / "../target/example/training.jpg";\n fs::path thumbnail_path = current_dir / "../target/example/thumbnail.jpg";\n\n try\n {\n c2pa::Context context;\n auto reader = Reader(context, output_path);\n\n auto manifest_store_json = reader.json();\n cout << "The new manifest is " << manifest_store_json << endl;\n\n // get the active manifest\n json manifest_store = json::parse(manifest_store_json);\n if (manifest_store.contains("active_manifest"))\n {\n string active_manifest = manifest_store["active_manifest"];\n json &manifest = manifest_store["manifests"][active_manifest];\n\n string identifier = manifest["thumbnail"]["identifier"];\n\n std::ofstream ofs(thumbnail_path, std::ios::binary);\n reader.get_resource(identifier, ofs);\n\n cout << "thumbnail written to " << thumbnail_path << endl;\n }\n }\n \n catch (c2pa::C2paException const &e)\n {\n cout << "C2PA Error: " << e.what() << endl;\n }\n}\n'})})]})}function d(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(l,{...e})}):l(e)}function h(e){const n={a:"a",code:"code",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.p,{children:"The example below shows how to get resources from manifest data using the Rust library."}),"\n",(0,a.jsxs)(n.p,{children:["Use ",(0,a.jsx)(n.a,{href:"https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.from_context",children:(0,a.jsx)(n.code,{children:"Reader::from_context"})})," to read a manifest and then use ",(0,a.jsx)(n.a,{href:"https://docs.rs/c2pa/latest/c2pa/struct.Reader.html#method.resource_to_stream",children:(0,a.jsx)(n.code,{children:"resource_to_stream"})})," to extract binary resources such as thumbnails:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-rust",children:'use c2pa::{Context, Reader, Result};\nuse std::{fs::File, io::Cursor};\n\nfn main() -> Result<()>
1 {\n let context = Context::new();\n let stream = File::open("path/to/file.jpg")?;\n let reader = Reader::from_context(context)\n .with_stream("image/jpeg", stream)?;\n\n if let Some(manifest) = reader.active_manifest() {\n if let Some(thumbnail_ref) = manifest.thumbnail_ref() {\n let mut thumbnail = Cursor::new(Vec::new());\n reader.resource_to_stream(&thumbnail_ref.identifier, &mut thumbnail)?;\n println!(\n "wrote thumbnail {} of size {}",\n thumbnail_ref.format,\n thumbnail.get_ref().len()\n );\n }\n }\n Ok(())\n}\n'})})]})}function f(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(h,{...e})}):h(e)}function m(e){const n={a:"a",code:"code",h3:"h3",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(n.p,{children:["After you obtain a ",(0,a.jsx)(n.a,{href:"https://contentauth.github.io/c2pa-js/interfaces/_contentauth_c2pa-web.Reader.html",children:(0,a.jsx)(n.code,{children:"Reader"})}),", use ",(0,a.jsx)(n.a,{href:"https://contentauth.github.io/c2pa-js/interfaces/_contentauth_c2pa-web.Reader.html#resourcetobytes",children:(0,a.jsx)(n.code,{children:"resourceToBytes(uri)"})})," to resolve a JUMBF URI from the manifest (for example ",(0,a.jsx)(n.code,{children:"activeManifest.thumbnail.identifier"}),") into raw bytes."]}),"\n",(0,a.jsx)(n.h3,{id:"thumbnail-from-the-active-manifest",children:"Thumbnail from the active manifest"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"import { createC2pa } from '@contentauth/c2pa-web';\nimport wasmSrc from '@contentauth/c2pa-web/resources/c2pa.wasm?url';\n\nasync function getActiveManifestThumbnail(url) {\n const c2pa = await createC2pa({ wasmSrc });\n\n const response = await fetch(url);\n const blob = await response.blob();\n\n const reader = await c2pa.reader.fromBlob(blob.type, blob);\n if (!reader) {\n c2pa.dispose();\n return;\n }\n\n const active = await reader.activeManifest();\n const uri = active.thumbnail?.identifier;\n if (!uri) {\n await reader.free();\n c2pa.dispose();\n return;\n }\n\n const bytes = await reader.resourceToBytes(uri);\n const thumbBlob = new Blob([bytes], {\n type: active.thumbnail?.format ?? 'image/jpeg',\n });\n const thumbUrl = URL.createObjectURL(thumbBlob);\n\n await reader.free();\n c2pa.dispose();\n}\n\nawait getActiveManifestThumbnail('/signed-image.jpg');\n"})}),"\n",(0,a.jsx)(n.h3,{id:"ingredient-resources",children:"Ingredient resources"}),"\n",(0,a.jsxs)(n.p,{children:["Ingredients can expose their own ",(0,a.jsx)(n.code,{children:"thumbnail"})," or other resource references. Inspect ",(0,a.jsx)(n.code,{children:"active.ingredients"})," (or the full ",(0,a.jsx)(n.a,{href:"https://contentauth.github.io/c2pa-js/interfaces/_contentauth_c2pa-web.Reader.html#manifeststore",children:(0,a.jsx)(n.code,{children:"manifestStore"})}),") and pass each ",(0,a.jsx)(n.code,{children:"identifier"})," string to ",(0,a.jsx)(n.code,{children:"resourceToBytes"}),"."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const store = await reader.manifestStore();\nconst label = store.active_manifest;\nconst manifest = label ? store.manifests[label] : undefined;\n\nfor (const ing of manifest?.ingredients ?? []) {\n if (ing.thumbnail?.identifier) {\n const data = await reader.resourceToBytes(ing.thumbnail.identifier);\n // use data (Uint8Array)\n }\n}\n"})})]})}function p(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(m,{...e})}):m(e)}function b(e){const n={code:"code",h3:"h3",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(n.p,{children:["Binary resources (thumbnails, icons, linked manifest blobs) are referenced by URI strings in the active manifest. Use ",(0,a.jsx)(n.code,{children:"Reader.resourceToAsset"})," to copy a resource to a file path or to a buffer."]}),"\n",(0,a.jsx)(n.h3,{id:"write-a-resource-to-a-file",children:"Write a resource to a file"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"import { Reader } from '@contentauth/c2pa-node';\nimport path from 'node:path';\n\nasync function writeThumbnail(assetPath, outputPath) {\n const reader = await Reader.fromAsset({ path: assetPath });\n if (!reader) {\n throw new Error('No C2PA manifest found for this asset.');\n }\n\n const manifest = reader.getActive();\n const uri = manifest?.thumbnail?.identifier;\n if (!uri) {\n throw new Error('Active manifest has no thumbnail.');\n }\n\n await reader.resourceToAsset(uri, { path: path.resolve(outputPath) });\n}\n"})}),"\n",(0,a.jsx)(n.h3,{id:"read-a-resource-into-a-buffer",children:"Read a resource into a buffer"}),"\n",(0,a.jsxs)(n.p,{children:["Pass a destination object with ",(0,a.jsx)(n.code,{children:"buffer: null"}),"."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"import { Reader } from '@contentauth/c2pa-node';\n\nasync function readResourceToBuffer(assetPath, uri) {\n const reader = await Reader.fromAsset({ path: assetPath });\n if (!reader) {\n throw new Error('No C2PA manifest found.');\n }\n\n const { buffer } = await reader.resourceToAsset(uri, { buffer: null });\n if (!buffer || buffer.length === 0) {\n throw new Error('Resource could not be read.');\n }\n return buffer;\n}\n"})}),"\n",(0,a.jsx)(n.h3,{id:"discover-uris-from-json",children:"Discover URIs from JSON"}),"\n",(0,a.jsxs)(n.p,{children:["You can inspect ",(0,a.jsx)(n.code,{children:"reader.json()"})," for the full manifest store, or use ",(0,a.jsx)(n.code,{children:"getActive()"})," for the active manifest only. Ingredient thumbnails use the same ",(0,a.jsx)(n.code,{children:"identifier"})," pattern as the claim thumbnail."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"import { Reader } from '@contentauth/c2pa-node';\n\nconst reader = await Reader.fromAsset({ path: 'signed.jpg' });\nif (!reader) {\n process.exit(0);\n}\n\nconst store = reader.json();\nconsole.log(JSON.stringify(store, null, 2));\n\nconst active = reader.getActive();\nfor (const ing of active?.ingredients ?? []) {\n if (ing.thumbnail?.identifier) {\n console.log('Ingredient thumbnail URI:', ing.thumbnail.identifier);\n }\n}\n"})})]})}function g(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(b,{...e})}):b(e)}
1const x={id:"get-resources",title:"Getting resources from a manifest"},j=void 0,v={},w=[{value:"Thumbnail from the active manifest",id:"thumbnail-from-the-active-manifest",level:3},{value:"Ingredient resources",id:"ingredient-resources",level:3},{value:"Write a resource to a file",id:"write-a-resource-to-a-file",level:3},{value:"Read a resource into a buffer",id:"read-a-resource-into-a-buffer",level:3},{value:"Discover URIs from JSON",id:"discover-uris-from-json",level:3}];function _(e){const n={p:"p",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.p,{children:"Manifest data can include binary resources such as thumbnail and icon images which are referenced by JUMBF URIs in manifest data."}),"\n",(0,a.jsxs)(i.A,{groupId:"programming-lang",queryString:"lang",children:[(0,a.jsx)(o.A,{value:"rust",label:"Rust",children:(0,a.jsx)(f,{name:"rust-read"})}),(0,a.jsx)(o.A,{value:"cpp",label:"C++",children:(0,a.jsx)(d,{name:"cpp-read"})}),(0,a.jsx)(o.A,{value:"python",label:"Python",default:!0,children:(0,a.jsx)(u,{name:"python-read"})}),(0,a.jsx)(o.A,{value:"js",label:"JavaScript",children:(0,a.jsx)(p,{name:"js-get-resources"})}),(0,a.jsx)(o.A,{value:"node",label:"Node.js",children:(0,a.jsx)(g,{name:"node-get-resources"})})]})]})}function y(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(_,{...e})}):_(e)}},4865:(e,n,t)=>{t.d(n,{A:()=>f});t(6540);var r=t(8215),a=t(7559),s=t(7751),i=t(3104),o=t(2303);const c={tabList:"tabList__CuJ",tabItem:"tabItem_LNqP"};var u=t(4848);function l({className:e}){const{selectedValue:n,selectValue:t,tabValues:a,block:o}=(0,s.uc)(),l=[],{blockElementScrollPositionUntilNextRender:d}=(0,i.a_)(),h=e=>{const r=e.currentTarget,s=l.indexOf(r),i=a[s].value;i!==n&&(d(r),t(i))},f=e=>{let n=null;switch(e.key){case"Enter":h(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,u.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":o},e),children:a.map(({value:e,label:t,attributes:a})=>(0,u.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{l.push(e)},onKeyDown:f,onClick:h,...a,className:(0,r.A)("tabs__item",c.tabItem,a?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function d({children:e}){return(0,u.jsx)("div",{className:"margin-top--md",children:e})}function h({className:e,children:n}){return(0,u.jsxs)("div",{className:(0,r.A)(a.G.tabs.container,"tabs-container",c.tabList),children:[(0,u.jsx)(l,{className:e}),(0,u.jsx)(d,{children:n})]})}function f(e){const n=(0,o.A)(),t=(0,s.OC)(e);return(0,u.jsx)(s.O_,{value:t,children:(0,u.jsx)(h,{className:e.className,children:(0,s.vT)(e.children)})},String(n))}},7751:(e,n,t)=>{t.d(n,{OC:()=>m,O_:()=>g,uc:()=>b,vT:()=>l});var r=t(6540),a=t(6347),s=t(205),i=t(7485),o=t(679),c=t(1682),u=t(4848);function l(e){return r.Children.toArray(e).filter(e=>"\n"!==e)}function d(e){const{values:n,children:t}=e;return(0,r.useMemo)(()=>{const e=n??function(e){return r.Children.toArray(e).flatMap(e=>{if(!e)return[];if((0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];const n="string"==typeof e.type?e.type:e.type.name;throw new Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:r}})=>({value:e,label:n,attributes:t,default:r}))}(t);return function(e){const n=(0,c.XI)(e,(e,n)=>e.value===n.value);if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[n,t])}function h({value:e,tabValues:n}){return n.some(n=>n.value===e)}function f({queryString:e=!1,groupId:n}){const t=(0,a.W6)(),s=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,i.aZ)(s),(0,r.useCallback)(e=>{if(!s)return;const n=new URLSearchParams(t.location.search);n.set(s,e),t.replace({...t.location,search:n.toString()})},[s,t])]}function m(e){const{defaultValue:n,queryString:t=!1,groupId:a}=e,i=d(e),[c,u]=(0,r.useState)(()=>function({defaultValue:e,tabValues:n}){if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!h({value:e,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const t=n.find(e=>e.default)??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:i})),[l,m]=f({queryString:t,groupId:a}),[p,b]=function({groupId:e}){const n=function(e){return e?`docusaurus.tab.${e}`:null}(e),[t,a]=(0,o.Dv)(n);return[t,(0,r.useCallback)(e=>{n&&a.set(e)},[n,a])]}({groupId:a}),g=(()=>{const e=l??p;return h({value:e,tabValues:i})?e:null})();(0,s.A)(()=>{g&&u(g)},[g]);return{selectedValue:c,selectValue:(0,r.useCallback)(e=>{if(!h({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);u(e),m(e),b(e)},[m,b,i]),tabValues:i,lazy:e.lazy??!1,block:e.block??!1}}const p=(0,r.createContext)(null);function b(){const e=r.useContext(p);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function g(e){return(0,u.jsx)(p.Provider,{value:e.value,children:e.children})}},8453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>o});var r=t(6540);const a={},s=r.createContext(a);function i(e){const n=r.useContext(s);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:i(e.components),r.createElement(s.Provider,{value:n},e.children)}},9365:(e,n,t)=>{t.d(n,{A:()=>c});t(6540);var r=t(8215),a=t(7751);const s={tabItem:"tabItem_Ymn6"};var i=t(4848);function o({children:e,className:n,hidden:t}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,r.A)(s.tabItem,n),hidden:t,children:e})}function c({children:e,className:n,value:t}){const{selectedValue:r,lazy:s}=(0,a.uc)(),c=t===r;return!c&&s?null:(0,i.jsx)(o,{className:n,hidden:!c,children:e})}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.