"""Extrae metadatos de un PDF con pypdf (OSINT pasiva sobre documentos propios).""" from pypdf import PdfReader def _iso_or_raw(reader: PdfReader, getter_name: str, raw_value): """Devuelve una fecha en ISO 8601 si pypdf la sabe parsear, sino el valor crudo. pypdf expone `creation_date`/`modification_date` (datetime) ademas del string crudo `/CreationDate`/`/ModDate` en formato `D:YYYYMMDDHHmmSS`. """ try: dt = getattr(reader.metadata, getter_name, None) except Exception: dt = None if dt is not None: try: return dt.isoformat() except Exception: pass return str(raw_value) if raw_value is not None else None def extract_pdf_metadata(pdf_path: str) -> dict: """Lee los metadatos del Document Info de un PDF. Abre el PDF con pypdf y extrae los campos estandar del diccionario de informacion (titulo, autor, creador, productor, fechas) mas el numero de paginas. Las fechas se devuelven en ISO 8601 cuando son parseables, en su valor crudo en caso contrario. No falla si el PDF esta cifrado: captura la excepcion, devuelve lo que pueda y rellena el campo `error`. Args: pdf_path: ruta al archivo PDF en disco. Returns: dict con las claves: title, author, creator, producer, creation_date, mod_date, num_pages, raw (dict con todo el doc info) y error (None si todo fue bien, mensaje de error en caso contrario). """ result = { "title": None, "author": None, "creator": None, "producer": None, "creation_date": None, "mod_date": None, "num_pages": None, "raw": {}, "error": None, } try: reader = PdfReader(pdf_path) # PDF cifrado: intentar abrir con password vacio (caso comun). if getattr(reader, "is_encrypted", False): try: reader.decrypt("") except Exception as exc: result["error"] = f"encrypted: {exc}" try: result["num_pages"] = len(reader.pages) except Exception as exc: result["error"] = result["error"] or f"pages: {exc}" meta = reader.metadata if meta is not None: result["title"] = str(meta.title) if meta.title is not None else None result["author"] = str(meta.author) if meta.author is not None else None result["creator"] = str(meta.creator) if meta.creator is not None else None result["producer"] = ( str(meta.producer) if meta.producer is not None else None ) result["creation_date"] = _iso_or_raw( reader, "creation_date", meta.get("/CreationDate") ) result["mod_date"] = _iso_or_raw( reader, "modification_date", meta.get("/ModDate") ) result["raw"] = {str(k): str(v) for k, v in meta.items()} except Exception as exc: result["error"] = result["error"] or str(exc) return result