#!/usr/bin/env python3
"""
A mettre dans le dossier ISNB_PDF/ à la fin avant envoie à l'imprimeur.

Post-traitement de PDF pour activer la surimpression UNIQUEMENT sur le noir pur,
en preservant la defonce pour toutes les autres couleurs (blanc, couleurs, gris,
noirs riches).

Methode :
  1. Ajout de deux ExtGState par page : "GSopOn" (surimpression active) et
     "GSopOff" (surimpression desactivee explicitement).
  2. Parsing du content stream pour reperer :
       - les operateurs de couleur (k, K, g, G, rg, RG, sc, SC, scn, SCN)
       - les operateurs q (gsave) et Q (grestore) pour suivre la profondeur graphique.
  3. Apres chaque operateur de couleur :
       - si la couleur est un noir pur (en CMYK ou gris), insertion de "/GSopOn gs"
       - sinon, insertion de "/GSopOff gs"
     Cela force chaque trace a avoir l'etat de surimpression correct, sans 
     dependre de l'heritage entre objets.

Usage : python3 set_black_overprint.py entree.pdf sortie.pdf
Pre-requis : pip install pikepdf
"""

import re
import sys
import pikepdf


# Operateur de couleur : capture la sequence "valeurs ... operateur"
COLOR_RE = re.compile(
    rb'(?<![A-Za-z0-9.])'
    rb'((?:[-+]?\d*\.?\d+\s+){4}[kK]'         # CMYK fill/stroke
    rb'|(?:[-+]?\d*\.?\d+\s+){3}(?:rg|RG)'    # RGB fill/stroke
    rb'|[-+]?\d*\.?\d+\s+[gG](?![A-Za-z])'    # Gray fill/stroke
    rb')'
)


def is_pure_black(op_bytes: bytes) -> bool:
    """True si l'operateur de couleur designe du noir pur (CMYK 0001, gray 0)."""
    s = op_bytes.decode("ascii", errors="ignore").strip()
    parts = s.split()
    if not parts:
        return False
    op_name = parts[-1]
    try:
        values = [float(v) for v in parts[:-1]]
    except ValueError:
        return False

    if op_name in ("k", "K"):  # CMYK
        return (
            len(values) == 4
            and values[0] <= 0.001
            and values[1] <= 0.001
            and values[2] <= 0.001
            and values[3] >= 0.999
        )
    if op_name in ("g", "G"):  # gray : 0 = noir
        return len(values) == 1 and values[0] <= 0.001
    if op_name in ("rg", "RG"):  # RGB : 0 0 0 = noir
        return len(values) == 3 and all(v <= 0.001 for v in values)
    return False


def transform_stream(data: bytes) -> bytes:
    """Insere /GSopOn gs ou /GSopOff gs apres chaque operateur de couleur."""
    out = bytearray()
    pos = 0
    for m in COLOR_RE.finditer(data):
        out.extend(data[pos:m.end()])
        if is_pure_black(m.group(1)):
            out.extend(b" /GSopOn gs")
        else:
            out.extend(b" /GSopOff gs")
        pos = m.end()
    out.extend(data[pos:])
    return bytes(out)


def add_extgstates(pdf: pikepdf.Pdf):
    """Ajoute GSopOn et GSopOff a toutes les pages."""
    gs_on = pikepdf.Dictionary(
        Type=pikepdf.Name("/ExtGState"),
        OP=True,
        op=True,
        OPM=1,
    )
    gs_off = pikepdf.Dictionary(
        Type=pikepdf.Name("/ExtGState"),
        OP=False,
        op=False,
    )

    on_ref = pdf.make_indirect(gs_on)
    off_ref = pdf.make_indirect(gs_off)

    for page in pdf.pages:
        if "/Resources" not in page:
            page.Resources = pikepdf.Dictionary()
        if "/ExtGState" not in page.Resources:
            page.Resources.ExtGState = pikepdf.Dictionary()
        page.Resources.ExtGState[pikepdf.Name("/GSopOn")] = on_ref
        page.Resources.ExtGState[pikepdf.Name("/GSopOff")] = off_ref


def set_black_overprint(input_pdf: str, output_pdf: str) -> int:
    pdf = pikepdf.open(input_pdf)
    add_extgstates(pdf)

    pages_modified = 0
    for page in pdf.pages:
        content = page.Contents
        if isinstance(content, pikepdf.Array):
            data = b"".join(c.read_bytes() for c in content)
        else:
            data = content.read_bytes()

        new_data = transform_stream(data)
        if new_data != data:
            new_stream = pdf.make_stream(new_data)
            page.Contents = new_stream
            pages_modified += 1

    pdf.save(output_pdf)
    pdf.close()
    return pages_modified


if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage : python3 set_black_overprint.py entree.pdf sortie.pdf")
        sys.exit(1)
    n = set_black_overprint(sys.argv[1], sys.argv[2])
    #n = set_black_overprint("095065409_ILTM.pdf","095065409_ILTM_surimpression.pdf")
    print("Fin de la conversion en surimpression.")
