#!/usr/bin/osascript -l JavaScript 
ObjC.import('Foundation');
ObjC.import('Vision');  /* OCR */
ObjC.import('PDFKit'); /* PDF */
ObjC.import('AppKit'); /* NSImage */

/* Verbesserte Version eines OCR-Scripts: 
   - verarbeitet Bilddateien und PDF
   - gibt die Textschnipsel von links oben nach rechts unten sortiert aus
   - Dateiname kann auf der Kommandozeile übergeben werden.
*/

function textFromImage(image) {
  const error = $();
  const languages = ["de-DE", "us-US"];
  const languagesObjC = languages.map(l => $(l));

  const request = $.VNRecognizeTextRequest.alloc.init;
  const imageSize = image.size;
  request.setRecognitionLanguages($(languagesObjC))
  request.usesLanguageCorrection = true;
 
  const reqArray = $.NSArray.arrayWithObject(request);
  const imageRequestHandler = 
    $.VNImageRequestHandler.alloc.initWithDataOptions(image.TIFFRepresentation, {});

  /* Texterkennung starten */
  const success = imageRequestHandler.performRequestsError(reqArray, error);
  if (!success) {
    console.log($(error.localizedDescription).js)
    return [];
  } else {
    /* Erkannte Textfragmente samt linker unterer Ecke in ein Array schreiben */
    const schnipsel = request.results.js;
    const OCRresult = schnipsel.map(s => {
      const bestHit = s.topCandidates(1).js[0];
           return { 
          "string":  bestHit.string.js,
          "origin" : { 
                       x: s.bottomLeft.x * imageSize.width,
                       y: s.bottomLeft.y * imageSize.height
                     } 
      }
    });
    return OCRresult;
  }
}

function OCRFile(dateiName) {
  const error = $();
  const uti = $();
  /*
  URL zur Datei besorgen und UTI bestimmen
  */
  const fileURL = $.NSURL.fileURLWithPath(dateiName);
  fileURL.getResourceValueForKeyError(uti, $.NSURLTypeIdentifierKey, error);

  let result = [];
  if (/jpeg|image|png/.test(uti.js)) {
    /* Bilder mit nur einer "Seite":
       Bild aus Datei laden und an OCR-Funktion schicken.
    */
     const image = $.NSImage.alloc.initWithContentsOfURL(fileURL);
     result = textFromImage(image);
  } else if (/pdf/.test(uti.js)) {
    /* PDF-Datei mit möglicherweise mehr als einer Seite:
       PDFDocument aus Datei laden
       für jede Seite die dataRepresentation laden
       in TIFF wandeln
       TIFF an OCR-Funktion übergeben
    */
    const pdfDoc = $.PDFDocument.alloc.initWithURL(fileURL);
    for (let i = 0; i< pdfDoc.pageCount; i++) {
      const rawData = pdfDoc.pageAtIndex(i).dataRepresentation;
      const image = $.NSImage.alloc.initWithData(rawData);
      result.push(...textFromImage(image));
    }
  }
  return result;
}

function comparePosition(a,b) {
  const diff = b.origin.y - a.origin.y;
  if (diff > -5 && diff < 5) {
    return a.origin.x - b.origin.x;
  } else {
    return diff;
  }
}

/* Übergebenen Text als extended attribute 
        "com.apple.metadata:kMDItemComment"
   an der übergebenen Datei speichern.
   Danach kann man mit Spotlight suchen 
   */ 
function addTextAsComment(text, file) {
  const XMPList = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<string>${text}</string>
</plist>`
  const cmd = `xattr -w com.apple.metadata:kMDItemComment '${XMPList}' "${file}"`;
  const curApp =  Application.currentApplication();
  curApp.includeStandardAdditions = true;
  curApp.doShellScript(cmd);
}

function saveToClipboard(txt) {
  const curApp = Application.currentApplication();
  curApp.includeStandardAdditions = true;
  curApp.setTheClipboardTo(txt);
}

/* 
  Einsprungspunkt: anonyme, selbst-ausführende Funktion.
  Name der Datei kann auf der Kommandozeile übergeben werden, 
  Default ist als `Hazel-Regel-PDF.pdf' vorgegeben.
*/
(() => {
  /* Kommandozeilenargumente besorgen.
     args[0..4]: "osascript", "-l", "JavaScript", Script-Datei
     args[5]: Fehlt oder dateiName
  */
  const args = $.NSProcessInfo.processInfo.arguments;
  const filename = (() => {
    if (args.count == 5) {
       return args.js[4].js;
    } else {
       return './Hazel-Regel-PDF.pdf'
    }
  })()
  const snippets = OCRFile(filename);
  const completeText = [];
  snippets.sort(comparePosition).forEach((s,index) => {
      completeText.push(s.string);
  })
  /* completeText enthält die Textschnipsel in korrekter Reihenfolge.
     Durch Zeilenumbruch getrennt in Zwischenablage schreiben.
  */
  console.log(completeText.join('\n'))
  saveToClipboard(completeText.join('\n'));
})()


