adesse

Signed testimonials for certifications.
Log | Files | Refs

commit 45818068a86bd0c6cb30e3de53e7ad7ad7d888c3
parent 616c746b100620cd96f2456f694e156fced617fe
Author: lash <dev@holbrook.no>
Date:   Sun,  6 Sep 2026 15:34:46 -0600

Complete parsing to attachment

Diffstat:
Madesse/attachment.py | 6++++++
Madesse/ns/__init__.py | 1+
Aadesse/ns/xmp.py | 10++++++++++
Madesse/util/xmp.py | 140++++++++++++++++++++++++++++++++-----------------------------------------------
Mtest/test_xmp.py | 8++++++--
5 files changed, 79 insertions(+), 86 deletions(-)

diff --git a/adesse/attachment.py b/adesse/attachment.py @@ -30,6 +30,12 @@ class Attachment(Entity): raise NotImplementedError() + def set_sum_from_urn(self, v): + if v[:11] != 'URN:sha256:': + raise ValueError('only sha256 urn supported') + return self.set_sum(bytes.fromhex(v[11:])) + + def apply(self, g): uu = to_uuid_uri(self.uu) g.add((uu, DC.format, Literal(self.mime))) diff --git a/adesse/ns/__init__.py b/adesse/ns/__init__.py @@ -7,3 +7,4 @@ from .inc import * from .wot import WOT from .df import DF from .pss import PSS +from .xmp import XMP diff --git a/adesse/ns/xmp.py b/adesse/ns/xmp.py @@ -0,0 +1,10 @@ +from .inc import * + + +class XMP(DefinedNamespace): + + CreateDate: URIRef + CreatorTool: URIRef + ModifyDate: URIRef + + _NS = Namespace('http://ns.adobe.com/xap/1.0/') diff --git a/adesse/util/xmp.py b/adesse/util/xmp.py @@ -1,43 +1,16 @@ import os import logging import hashlib +import mimetypes +import datetime import lxml.etree from rdflib import Graph, Namespace, RDF, URIRef, DC, Literal from adesse import Attachment +from adesse.ns import XMP logg = logging.getLogger('util.xmp') -EXIFTOOL_XMP = Namespace('http://ns.exiftool.org/XMP/XMP-xmp/1.0/') -EXIFTOOL_XMPPDF = Namespace('http://ns.exiftool.org/XMP/XMP-pdf/1.0/') -EXIFTOOL_XMPDC = Namespace('http://ns.exiftool.org/XMP/XMP-dc/1.0/') -EXIFTOOL_SYSTEM = Namespace('http://ns.exiftool.org/File/System/1.0/') - - - -def _from_xml(item, o, exiftool=False): - parsens = DC - if exiftool: - parsens = EXIFTOOL_XMPDC - title = o.value(item, parsens.term('title')) - if title == None: - title = o.value(item, parsens.term('Title')) - logg.debug('have title {} {} {}'.format(item, parsens, title)) - desc = o.value(item, parsens.term('subject')) - if desc == None: - desc = o.value(item, parsens.term('Subject')) - logg.debug('have desc {} {} {}'.format(item, parsens, desc)) -# o = Attachment(title) - - -def check_file(fp, strict=False): - logg.debug('checi file {}'.format(fp)) - os.stat(fp) - if not strict: - return fp - return fp - - def _parse_xmp(xmp_file, resource_file, search_dir=None): o = lxml.etree.parse(xmp_file) @@ -66,8 +39,10 @@ def _parse_xmp(xmp_file, resource_file, search_dir=None): for v in r.findall('rdf:Description', namespaces={'rdf': str(RDF)}): v.set('{{{}}}about'.format(str(RDF)), uu) + mime = mimetypes.guess_type(resource_file) + s = lxml.etree.tostring(r) - return (uu, s,) + return (uu, mime, s,) @@ -76,7 +51,7 @@ def rdfn(v): return URIRef(s) -def _get_alt_lang(sbj, prd, o): +def _get_alt_lang(sbj, prd, o, lang=None): v = o.value(sbj, prd) typ = o.value(v, RDF.type) r = None @@ -85,7 +60,7 @@ def _get_alt_lang(sbj, prd, o): while i: v = o.value(v, rdfn(i)) if isinstance(v, Literal): - if v.language == 'x-default': + if v.language == 'x-default' or (v.language != None and v.language == lang): r = v.value i = 0 else: @@ -95,12 +70,41 @@ def _get_alt_lang(sbj, prd, o): return r -def parse_title(sbj, o): - return _get_alt_lang(sbj, DC.title, o) +def _get_bag(sbj, prd, o): + v = o.value(sbj, prd) + typ = o.value(v, RDF.type) + r = [] + if typ == RDF.Bag: + i = 1 + while i: + v = o.value(v, rdfn(i)) + if v == None: + break + r.append(v.value) + i += 1 + return r + + + +def parse_title(sbj, o, lang=None): + return _get_alt_lang(sbj, DC.title, o, lang=lang) + +def parse_description(sbj, o, lang=None): + return _get_alt_lang(sbj, DC.description, o, lang=lang) -def parse_description(sbj, o): - return _get_alt_lang(sbj, DC.description, o) + +def parse_date(sbj, o): + v = o.value(sbj, XMP.CreateDate) + return datetime.datetime.fromisoformat(v.value) + + +def scan_lang(sbj, o): + r = None + v = _get_bag(sbj, DC.language, o) + if len(v) == 1: + r = v[0] + return (r, v,) def attachment_from_xmp(xml_file, resource_file=None, search_dir=None): @@ -108,50 +112,18 @@ def attachment_from_xmp(xml_file, resource_file=None, search_dir=None): search_dir = ['.'] r = _parse_xmp(xml_file, resource_file=resource_file, search_dir=search_dir) o = Graph() - sbj = URIRef(r[0]) - o.parse(r[1], format='xml') - title = parse_title(sbj, o) - desc = parse_description(sbj, o) - logg.debug('have title {} desc {}'.format(title, desc)) - - -#def attachment_from_xmp(filepath, search_dir=None): -# if search_dir == None: -# search_dir = ['.'] -# state = { -# 'exif': False, -# 'xmp': False, -# } -# items = [] -# o = Graph() -# r = o.parse(filepath, format='xml') -# #o.bind('XMP-dc', DC, replace=True) -# for v in o.namespace_manager.namespaces(): -# logg.debug('have namespace {}'.format(v)) -# k = v[0] -# if 'ExifTool' in k: -# state['exif'] = True -# if 'xmp' in k: -# state['xmp'] = True -# if not state['xmp']: -# raise AttributeError('not an xmp doument') -# for v in o.subjects(): -# fp = None -# for d in search_dir: -# fp = os.path.join(d, v) -# try: -# fp = check_file(fp) -# break -# except FileNotFoundError: -# fp = None -# if fp == None: -# continue -# -# if v not in items: -# items.append(v) -# logg.debug('added subject {} {}'.format(v, fp)) -# -# r = [] -# for v in items: -# r.append(_from_xml(v, o, exiftool=state['exif'])) -# return r + z = r[0] + sbj = URIRef(z) + mime = r[1] + o.parse(r[2], format='xml') + (default_lang, langs) = scan_lang(sbj, o) + title = parse_title(sbj, o, lang=default_lang) + desc = parse_description(sbj, o, lang=default_lang) + date = parse_date(sbj, o) + # TODO: prefer dc:format + mime = str(r[1][0]) + logg.debug('have title {} desc {} mime {}'.format(title, desc, mime)) + slug = os.path.splitext(xml_file)[0] + v = Attachment(title, desc, date, mime, lang=default_lang) + v.set_sum_from_urn(z) + return v diff --git a/test/test_xmp.py b/test/test_xmp.py @@ -5,9 +5,10 @@ import os import logging from rdflib.term import URIRef +from rdflib import Graph -from adesse.ns import XMP from adesse.util import xmp +from adesse import Serializer logging.basicConfig(level=logging.DEBUG) @@ -22,7 +23,10 @@ class TestXmpLoad(unittest.TestCase): def test_parse(self): fp = os.path.join(thisdir, 'import.xmp') - xmp.attachment_from_xmp(fp, 'import.pdf', search_dir=[thisdir]) + r = xmp.attachment_from_xmp(fp, 'import.pdf', search_dir=[thisdir]) + z = Serializer() + r.apply(z) + print(z) if __name__ == '__main__':