Interceptor-Beispiel: Dokumente splitten - Seiten trennen in Embedded Documents

Seitentrennung mit Embedded Documents erscheint rein technisch gesehen als ein einfaches Thema.

Kompliziert wird es, wenn man fachlich definieren möchte, was sich jeder einzelne im konkreten Fall unter dem Splitting von PDFs vorstellt bzw. im Projekt umsetzen möchte:

  • Wohin soll die X neuen Dokumente gesplittet werden?
    • In den gleichen Ordner?
    • Weil es Eingangsbestellungen sind ist X Auftragsordner mit dem gleichen Datum, welche gleich mit angelegt werden müssen?
    • Weil es Eingangspost ist in X Post-Workflows?
  • Was passiert mit dem Original?
    • Bleibt es das erste Dokument, weil es eines unter vielen ist?
    • Ist es ein wichtiges Original und muss separat aufbewahrt werden?
    • Soll es gelöscht werden?
  • Beziehung zwischen den Dokumenten: Sollen die ehemals zusammengesetzten und nun gesplittteten X Dokumente in enaio®/yuuvis® verknüpft bleiben? Wenn ja, wie?
    • Über Metadaten (gleicher Schlüssel, welcher generiert werden muss, im Feld „Splitting“ o. ä.)
    • Über Notizverknüpfungen
  • Wie ergeben sich die Metadaten der neuen Teilbelege?
    • Alle gleich?
    • Alle gleich, aber es wird ine einem konkreten Feld passend zur Objektdefinition durchgezählt?
    • OCR des Barcodes pro erster Seite?

Selbstverständlich muss es nicht kompliziert sein. Zusammen mit Komm.ONE haben wir eine beispielhafte Umsetzung geschaffen, welche es Ihnen erlaubt Dokumente über die Seitenleiste zu splitten:

In unserem Beispiel-Interceptor können die User Checkboxen auf den jeweils neuen ersten Seiten eines jeden Teilbelegs setzen und drücken zum Aufteilen das Scheren-Symbol.

Beispiel-Code

Der Interceptor kann individuell angepasst und bei Bedarf wie gewohnt in .-.\service-manager\data\ed\public\interceptors abgelegt werden.

Hinweis: In diesem Fall werden gesplittete Seiten direkt neben dem aktuellen Dokument ohne spezielle Verschlagwortung abgelegt. Ein weiterentwickelte und für Baden-Württemberg standardisierte Version davon stellt der enaio®- und yuuvis®-Partner Komm.ONE ihren Kunden bereit.

Alle anderen können sich im Projekt anhand dieses Beispieles Anregungen holen:

let currentInfo = null;
let currentViewer = null;

let extractPagesInterceptor = {
  initEditorConfiguration(instance, config) {
    let { documentViewer, annotationManager } = instance.Core;

    let getMetadata = async (typeId, id) => {
      let request = {
        query: {
          objectTypeId: typeId,
          fields: {
            OBJECT_ID: {
              internalName: 'OBJECT_ID',
              value: id,
              system: true,
            },
          },
          result_config: {
            fieldsschema: [
              {
                internalName: 'SDSTA_ID',
                system: true,
              },
              {
                internalName: 'SDREG_ID',
                system: true,
              },
            ],
            fieldsschema_mode: 'ALL',
            maxhits: 1,
          },
        },
      };

      const rawResponse = await fetch(`/api/dms/objects/search/native`, {
        method: 'POST',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(request),
      });
      let result = await rawResponse.json();
      return result.objects[0];
    };

    let insert = async (properties, blob) => {
      let data = {
        objects: [
          {
            properties: properties,
            contentStreams: [
              {
                mimeType: 'application/pdf',
                fileName: 'content.pdf',
                cid: 'cid_1',
              },
            ],
          },
        ],
      };

      const formData = new FormData();
      formData.append('data', new Blob([JSON.stringify(data)], { type: 'application/json' }), 'data.json');

      formData.append('cid_1', blob, 'content.pdf');

      const rawResponse = await fetch(`/api/dms/objects`, {
        method: 'POST',
        headers: {
          Accept: 'application/json',
        },
        body: formData,
      });
      return await rawResponse.json();
    };

    let confirm = (ranges) => {
      return new Promise(async (resolve, reject) => {
        let ui = instance.UI;

        let message = 'Das Dokument wird bei den folgenden Seiten aufgetrennt: \r\n\r\n';

        for (let range of ranges) {
          message += `- Von Seite ${range.start} bis ${range.end}\r\n`;
        }

        let result = await ui.showWarningMessage({
          title: 'Dokument auftrennen',
          message: message,
          confirmBtnText: 'Aufteilen',
          secondaryBtnText: 'Abbrechen',
          onConfirm: async () => {
            resolve(true);
          },
          onSecondary: async () => {
            resolve(false);
          },
          onCancel: () => {
            resolve(false);
          },
        });
      });
    };

    let success = (ranges) => {
        return new Promise(async (resolve, reject) => {
          let ui = instance.UI;

          let message = 'Das Dokument wurde erfolgreich aufgetrennt';

          let result = await ui.showWarningMessage({
            title: 'Auftrennung abgeschlossen',
            message: message,
            confirmBtnText: 'ok',
            onConfirm: async () => {
              resolve(true);
            },
            onCancel: () => {
              resolve(false);
            },
          });
        });
      };

    let extract = (from, to) => {
      return new Promise(async (resolve, reject) => {
        let doc = documentViewer.getDocument();

        let pagesToExtract = [];
        for (let i = from; i <= to; i++) {
          pagesToExtract.push(i);
        }

        let annotList = annotationManager.getAnnotationsList().filter((annot) => pagesToExtract.indexOf(annot.PageNumber) > -1);
        let xfdfString = await annotationManager.exportAnnotations({ annotList });
        let data = await doc.extractPages(pagesToExtract, xfdfString);
        let arr = new Uint8Array(data);

        let blob = new Blob([arr], { type: 'application/pdf' });
        resolve(blob);
      });
    };

    let pad = (num, places) => {
      return String(num).padStart(places, '0');
    };

    let onClick = async (selectedPageNumbers) => {
      if (selectedPageNumbers.length < 2) {
        instance.UI.showErrorMessage("Selectieren Sie mindestens zwei Seiten");
        return;
      }

      let pageCount = documentViewer.getPageCount();
      let ranges = [];

      for (let i = 0; i < selectedPageNumbers.length; i++) {
        let start = selectedPageNumbers[i];
        let end = selectedPageNumbers.length > i + 1 ? selectedPageNumbers[i + 1] - 1 : pageCount;

        ranges.push({ start: start, end: end });
      }

      let result = await confirm(ranges);
      if (!result) {
        return;
      }

      let sourceMetadata = await getMetadata(currentInfo.typeId, currentInfo.id);

      for (range of ranges) {
        let blob = await extract(range.start, range.end);

        let parentId = sourceMetadata.properties['system:SDREG_ID'].value != "0" ? sourceMetadata.properties['system:SDREG_ID'].value : sourceMetadata.properties['system:SDSTA_ID'].value;

        let properties = {
          'system:parentId': {
            value: parentId,
          },
          'system:objectTypeId': {
            value: currentInfo.typeId
          },
          //Name: { value: sourceMetadata.properties.Name.value + ' ' + pad(range.start, 4) + ' - ' + pad(range.end, 4) },
          //Type: { value: sourceMetadata.properties.Type.value },
        };

        for(let name of Object.keys(sourceMetadata.properties)){
            if(!name.startsWith("system:")){
                properties[name] = sourceMetadata.properties[name];
            }
        }

        await insert(properties, blob);
      }
      await success(ranges);
    };

    let buttons = [
      {
        type: 'customPageOperation',
        header: 'enaio',
        dataElement: 'customPageOperations',
        operations: [
          {
            title: 'Dokument auftrennen',
            img: '../../interceptors/scissors-solid.svg',
            onClick: async (selectedPageNumbers) => {
              await onClick(selectedPageNumbers);
            },
            dataElement: 'customPageOperationButton',
          },
        ],
      },
      { type: 'divider' },
    ];

    //instance.UI.pageManipulationOverlay.add(buttons);
    instance.UI.multiPageManipulationControls.add(buttons);
  },

  async updateEditorConfiguration(instance, info, config, viewer) {

    currentInfo = info;
    currentViewer = viewer;
    console.log('set currentInfo');
    instance.UI.enableElements(['thumbnailControl', 'documentControl']);
    instance.UI.enableFeatures(['ThumbnailMultiselect']);
    instance.UI.openElements(['leftPanel']);
  },
};

window.ed.registerInterceptor(extractPagesInterceptor);
1 „Gefällt mir“

Genau was wir aktuell benötigen um einen Bereich von TIFF auf PDF Dokumente umstellen zu können. Haben uns bisher mit der „Seiten extrahieren“ Funktion beholfen, so ist es allerdings deutlich eleganter. :slight_smile:

1 „Gefällt mir“

In der angepassten Version für Baden-Württemberg haben wir die Trennung folgendermaßen verändert.

  • es wird pro Vorgang nur ein Dokument mit allen markierten Seiten herausgetrennt
  • das Original-Dokument wird um die markierten Seiten reduziert
  • Indexdatenmaske des neuen Dokuments wird nach Ablage geöffnet (funktioniert nicht, wenn Embedded Documents als eigenständige Seite im Browser geöffnet ist). Wie im Beispiel werden die Dokumente zunächst auch am identischen Standort mit identischen Indexdaten abgespeichert.
2 „Gefällt mir“

Hallo @danielstraub, vielen Dank für Eure zentrale Pflege und coole Erweiterung!

Hi @danielstraub!

Deinen zweiten Punkt, dass das Originaldokument um die markierten Seiten reduziert wird, möchte ich nochmal aufgreifen.

Gerade im Bezug auf den elektronischen Posteingang ist es super hilfreich, dass das Originaldokument nicht durch das Auftrennen verändert wird! Sind in einem Poststück zwei unterschiedliche Dokumente enthalten (bspw. Antrag 1 und Antrag 2 kommen in einem Brief an, müssen aber separat abgelegt werden) wäre der Originalzustand des Poststücks nicht mehr nachvollziehbar und eine Beweisführung im Ernstfall schwierig.

Ist mit Sicherheit nicht an der Tagesordnung allerdings wäre eine Auswahlmöglichkeit, ähnlich der „Seiten extrahieren“ Funktion, bestimmt eine gute Lösung. :slight_smile:
image

Hallo @mm,

danke für deine Anmerkung.
Der Interceptor ist bei uns in ersten Kundenprojekten entstanden. Dort gab es den Bedarf noch nicht. Eine Auswahlmöglichkeit ist jedoch ein guter Weg. Wir werden das noch in den Interceptor mit einbauen.

1 „Gefällt mir“

Hallo,

beim testen des Interceptors ist uns aufgefallen das die Seitennavigation immer gleich geöffnet wird. Ist das so gewollt oder ist die Einbindung bei uns fehlerhaft?

VG
Ragnar Zumbeel

Das ist Absicht, aber ich glaube, das könnte mit dem Entfernen dieser Zeile nach belieben geändert werden:

1 „Gefällt mir“

Edit: Hab den enaio service-manager neu gestartet und es funktioniert wieder?!

Hallo zusammen,

vielleicht kann jemand helfen, aktuell erhalte ich beim Versuch die Seiten einer PDF-Datei zu trennen folgende Fehlermeldung:

image

HTTP-Code: 500

Bin ratlos was das sein könnte, am Script wurde nichts geändert. Jemand eine Idee?

1 „Gefällt mir“

Hi @mm,

ich würde vermuten, dass der AppConnector ein Problem hatte. Kannst du das osrest.log prüfen, ob du hier einen Fehlereintrag findest? Ansonsten kann es beim Gateway auch sein, dass er einen 500 Fehlercode statt einen 503 sendet, wenn der dahinterliegende Service nicht verfügbar ist. Daher das Gateway Log vielleicht zur Sicherheit auch noch prüfen.

Hi zusammen, beobachte aktuell ein komisches Verhalten.

Mein Ursprungsdokument (noch nicht aufgetrennt) hat folgende Objekttypen und ist als modulübergreifend eingerichtet:

image

Das Dokument wird bei einem Doppelklick extern im Acrobat Reader geöffnet.

Trenne ich nun das Dokument auf, erhalten die neu eingefügten Dokumente aber folgende Objekttypen:

image

Sind somit Bilder und werden in der internen Bilderanzeige geöffnet.

Kann ich steuern, welche Objekttypen das Script verwendet bzw. weiß jemand wo ich diese Info beim Insert mitgeben kann? Oder kann ich grundsätzlich irgendwo einstellen, wie die Dokumente bei einem Doppelklick geöffnet werden?

Danke für Eure Gedanken und viele Grüße
Marc

Hallo @mm,
das neu eingefügte Dokument wird automatisch mit dem Standard-Haupttyp (in deinem Fall offensichtlich 2) importiert. Um dies zu verhindern, sollte beim Insert der gewünschte Haupttyp mitgegeben werden. Dies kann im Interceptor als Eränzung bei den „properties“ erfolgen. In deinem Fall könnte das so aussehen:

'system:mainType': {
   value: '4',
},