// Copyright (C) 2003-2026, Foxit Software Inc..
// All Rights Reserved.
//
// http://www.foxitsoftware.com
//
// The following code is copyrighted and contains proprietary information and trade secrets of Foxit Software Inc..
// You cannot distribute any part of Foxit PDF SDK to any third party or general public,
// unless there is a separate license agreement with Foxit Software Inc. which explicitly grants you such rights.
//
// This file contains an example to demonstrate how to use Foxit PDF SDK to compare pdf page with the other.


const FSDK = require("@foxitsoftware/foxit-pdf-sdk-node");
const fs = require('fs');
const os = require('os');
const path = require('path');
const util = require('util');

var sn = "bPxThFlBAYifO3yiW0C51bKbjPap1zHaf5aG5sX378TjFvB7Q0802A==";
var key = "8f3g1sGNtR8NAgfSNv+BvaJWy2fnrw36NZvzh8GUxYXvs+/YczAk4e34yqlOnmL4asgxGMO7+rlOcSHUSSKagx5g4G7VEXfkYLXnDDx77FOOedZPrFFcOCu1vT4+wOKzy1zkORq9cKtiFf44byH5sfRqf+OvELjep2teT9eF9yPCsSw2vRKljXlPy40DMP9XX1O5OTe+mSG3RvwKUMb0+RpG3YobM87j+/ynTDlJMMXw4vgwuTb1ZuAtlvkTdFz7HDLV81C6TYnn73ddHeGD3qYWBVsD4uJkDwyyH/SWr7QX0HSgVg8HSZqnab1VGdAUYxKY2vGWeOTJdu6qDGb5ZEb9O3kZZg+9LYAmUKzDfCYwfIsYZ8Y9LPC4wMx70kIq7JpNnzPvigrdshV8/ew1F0+qP/P5Z1EB53vDVrp2LVSuL873Wv4g3PzjZlNH+1qgT87hC+Buto4qsSUfP5Y2z0uApu+c/TfttCXbeMLSsw9TY1HQD3mkrZExMaQqRrAuXAcZWdvBnVuG3DCZmm8q0DLxJiCfYfViAU/AkbeKMIGaTJdrvIXBcssfde4nmU6qr0M4x4+Hs2ESddtY2QRtLAUBq/7h7GJlJHkEZr8ueaUy/6D/2vvAWz6wMLgmjlPbDTLqzfnCJylHCPtcJmu1SPPT9v1VreBX3+zNWcIT8HueyHEiUniqzwWkRKaotrnNb/zhnHLPpgaBSwKjyi8rOzzVwu/nY84G9ye4jsVjmkIRItKDrW7z22iS+QQuTwfqLVuMB0OD4jMkPCC2slzPfEeZnuh4+Rewln2qLRJVgI72zJ20OBWAelGSpO1Ii2Y3gOvojdmtUwx6R3Z1qFNWTTB25o9ytcux8BLOtTCkA8T8xaT4N339OTCsu/TmfahIDixKvr8cDa6F2WkBT8SOfv64W9PgiNv5B/BMOD2VWYaAl0N7yD0NkYYg/cDWRC4dfpdeip1jrr0k3mND0oSpIkxC+FicZbaYYwRdtJ9OSVWJ5OE0voLv8HgLze8mw6s4TrN3z+a2NQCveMa2VRN7YcEF7agPrvb/qU+otMwHae/wM7+f+HFAaOW+WTfzfxIVFB8/A1DN0NiEX+em4G7nO5F5ZcvfYPpaaioe8Nn33BbVLtMjXqsOBeM8AXiVfpD6YO75j7BqnBUPnivvD8BxJaaIQZvVSuQmvGm0ynt03IYwE8Rq+jYsunvsZ86jh5BiUR+Nm7sK0Qe9BdZn/7jdqohMv9czfMGUAscX+xBMunFbDpcRbWVpUADcm209L8Ndek2XY7YlCMxPgPVNvfWRF2jxehib9Tpd7zB5ZIDfAEH9zrpLfp29DLvCh2Yx8mH7xxdKG1O9Q0GdHphYHhej6smAcPm68R0GUQaTOLfXqNpueGJC1GAOI3Y4swjyt7jMTWcibnKLzwukWE5rnEDhn5n64JnyUM0ZFkRmtOiV";

const input_path = "../input_files/";
const output_path = "../output_files/";


// get current date time
function GetLocalDateTime() {
  const date = new Date();
  let timezone_hour = date.getTimezoneOffset() / 60 * -1;
  let timezone_minute = Math.abs(date.getTimezoneOffset() % 60);
  let datetime = new FSDK.DateTime(date.getFullYear(), date.getMonth() + 1, date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds(), timezone_hour, timezone_minute);
  return datetime;
}

function RandomUID() {
  const characters = "0123456789qwertyuiopasdfghjklzxcvbnm";
  let uuid = "";

  for (let n = 0; n < 16; n++) {
    let uuidTemp = "";
    const b = Math.floor(Math.random() * 255);

    switch (n) {
      case 6:
        uuidTemp = '4' + (b % 15).toString(16);
        break;
      case 8:
        const randomCharIndex = Math.floor(Math.random() * characters.length);
        uuidTemp = characters[randomCharIndex] + (b % 15).toString(16);
        break;
      default:
        let hex = b.toString(16);
        if (hex.length < 2) {
          hex = '0' + hex;
        }
        uuidTemp = hex;
        break;
    }

    uuid += uuidTemp;
    if (n === 3 || n === 5 || n === 7 || n === 9) {
      uuid += '-';
    }
  }
  return uuid;
}

function CreateInsertStamp(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let rect_stamp = new FSDK.RectF();
  let rect_size = rects.GetSize();
  if (rect_size > 0) {
    let item = rects.GetAt(0);
    rect_stamp.left = item.left;
    rect_stamp.top = item.top-4 ;
    rect_stamp.right = rect_stamp.left + 4;
    rect_stamp.bottom = rect_stamp.top - 8;
  }

  let image = new FSDK.Image(input_path + "pdfcompare/insert_stamp.png");
  let stamp = new FSDK.Stamp(page.AddAnnot(FSDK.Annot.e_Stamp, rect_stamp));
  stamp.SetContent(ws_contents);
  stamp.SetBorderColor(color);
  stamp.SetSubject(ws_obj_type);
  stamp.SetTitle(ws_type);
  stamp.SetCreationDateTime(GetLocalDateTime());
  stamp.SetModifiedDateTime(GetLocalDateTime());
  stamp.SetUniqueID(RandomUID());
  stamp.SetImage(image, 0, 0);

  stamp.ResetAppearanceStream();
}

function CreateSquigglyRect(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let squiggly = new FSDK.Squiggly(page.AddAnnot(FSDK.Annot.e_Squiggly, new FSDK.RectF()));
  squiggly.SetContent(ws_contents);

  let quad_points_array = new FSDK.QuadPointsArray();
  let rect_size = rects.GetSize();
  for (let i=0; i<rect_size; i++) {
    let item = rects.GetAt(i);
    let quad_points = new FSDK.QuadPoints();
    quad_points.first = new FSDK.PointF(item.left, item.top);
    quad_points.second = new FSDK.PointF(item.right, item.top);
    quad_points.third = new FSDK.PointF(item.left, item.bottom);
    quad_points.fourth = new FSDK.PointF(item.right, item.bottom);
    quad_points_array.Add(quad_points);
  }
  squiggly.SetQuadPoints(quad_points_array);

  squiggly.SetBorderColor(color);
  squiggly.SetSubject(ws_obj_type);
  squiggly.SetTitle(ws_type);
  squiggly.SetCreationDateTime(GetLocalDateTime());
  squiggly.SetModifiedDateTime(GetLocalDateTime());
  squiggly.SetUniqueID(RandomUID());

  squiggly.ResetAppearanceStream();
}

function CreateDeleteTextStamp(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let rect_stamp = new FSDK.RectF();
  let rect_size = rects.GetSize();
  if (rect_size > 0) {
    let item = rects.GetAt(0);
    rect_stamp.left = item.left;
    rect_stamp.top = item.top + 12;
    rect_stamp.right = rect_stamp.left + 9;
    rect_stamp.bottom = rect_stamp.top - 12;
  }

  let image = new FSDK.Image(input_path + "pdfcompare/delete_stamp.png");

  let stamp = new FSDK.Stamp(page.AddAnnot(FSDK.Annot.e_Stamp, rect_stamp));
  stamp.SetContent(ws_contents);
  stamp.SetBorderColor(color);
  stamp.SetSubject(ws_obj_type);
  stamp.SetTitle(ws_type);
  stamp.SetCreationDateTime(GetLocalDateTime());
  stamp.SetModifiedDateTime(GetLocalDateTime());
  stamp.SetUniqueID(RandomUID());
  stamp.SetImage(image, 0, 0);

  stamp.ResetAppearanceStream();
}

function CreateDeleteText(page, rects, color, ws_contents, ws_type, ws_obj_type) {
  let strikeout = new FSDK.StrikeOut(page.AddAnnot(FSDK.Annot.e_StrikeOut, new FSDK.RectF()));
  strikeout.SetContent(ws_contents);

  let quad_points_array = new FSDK.QuadPointsArray();
  let rect_size = rects.GetSize();
  for (let i = 0; i<rect_size; i++) {
    let item = rects.GetAt(i);
    let quad_points = new FSDK.QuadPoints();
    quad_points.first = new FSDK.PointF(item.left, item.top);
    quad_points.second = new FSDK.PointF(item.right, item.top);
    quad_points.third = new FSDK.PointF(item.left, item.bottom);
    quad_points.fourth = new FSDK.PointF(item.right, item.bottom);
    quad_points_array.Add(quad_points);
  }
  strikeout.SetQuadPoints(quad_points_array);

  strikeout.SetBorderColor(color);
  strikeout.SetSubject(ws_obj_type);
  strikeout.SetTitle(ws_type);
  strikeout.SetCreationDateTime(GetLocalDateTime());
  strikeout.SetModifiedDateTime(GetLocalDateTime());
  strikeout.SetUniqueID(RandomUID());

  strikeout.ResetAppearanceStream();
}

let error_code = FSDK.Library.Initialize(sn, key);
if (FSDK.e_ErrSuccess != error_code) {
  if (FSDK.e_ErrInvalidLicense == error_code)
    console.log("[Failed] Current used Foxit PDF SDK key information is invalid.");
  else
    console.log("Library Initialize Error: %d", FSDK.e_ErrSuccess);
  return;
}

let output_directory = output_path + "pdfcompare/";
if (!fs.existsSync(output_directory))  {
  fs.mkdirSync(output_directory);
}




let input_base_file = input_path + "pdfcompare/test_base.pdf";
let input_compared_file = input_path + "pdfcompare/test_compared.pdf";

try {
  let base_doc = new FSDK.PDFDoc(input_base_file);
  let error_code = base_doc.Load("");
  if (error_code != FSDK.e_ErrSuccess) {
    console.log("The Doc [%s] Error: %d\n", input_base_file, error_code);
    return 1;
  }

  let compared_doc = new FSDK.PDFDoc(input_compared_file);
  error_code = compared_doc.Load("");
  if (error_code != FSDK.e_ErrSuccess) {
    console.log("The Doc [%s] Error: %d\n", input_compared_file, error_code);
    return 1;
  }

  let comparison = new FSDK.Comparison(base_doc, compared_doc);
  let result = comparison.DoCompare(0, 0, FSDK.Comparison.e_CompareTypeText);
  let old_info = result.base_doc_results;
  let new_info = result.compared_doc_results;
  let = result.compared_doc_results;
  let old_info_size = old_info.GetSize();
  let new_info_size = new_info.GetSize();
  let page_base = base_doc.GetPage(0);
  let page = compared_doc.GetPage(0);
  for (let i = 0; i < old_info_size; i++) {
    let item = old_info.GetAt(i);
    let type = item.type;
    if (type == FSDK.CompareResultInfo.e_CompareResultTypeDeleteText) {
      let res_string = `\"${item.diff_contents}\"`;
      CreateDeleteText(page_base, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeInsertText) {
      let res_string = `\"${item.diff_contents}\"`;
      CreateInsertStamp(page_base, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeReplaceText) {
      let res_string = `[New]: \"${new_info.GetAt(i).diff_contents}\"\r\n[Old]: \"${item.diff_contents}\"`;
      CreateSquigglyRect(page_base, item.rect_array, 0xe7651a, res_string, "Compare : Replace",  "Text");
    }
  }
  for (let i=0; i<new_info_size; i++) {
    let item = new_info.GetAt(i);
    let type = item.type;
    if (type == FSDK.CompareResultInfo.e_CompareResultTypeDeleteText) {
      let res_string = `\"${item.diff_contents}\"`;      
      CreateDeleteTextStamp(page, item.rect_array, 0xff0000, res_string, "Compare : Delete", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeInsertText) {
      let res_string = `\"${item.diff_contents}\"`;
      CreateDeleteText(page, item.rect_array, 0x0000ff, res_string, "Compare : Insert", "Text");
    } else if (type == FSDK.CompareResultInfo.e_CompareResultTypeReplaceText) {      
      let res_string = `[New]: \"${new_info.GetAt(i).diff_contents}\"\r\n[Old]: \"${item.diff_contents}\"`;
      CreateSquigglyRect(page, item.rect_array, 0xe7651a, res_string, "Compare : Replace", "Text");
    }
  }
  base_doc.SaveAs(output_directory + "old.pdf", FSDK.PDFDoc.e_SaveFlagNormal);
  compared_doc.SaveAs(output_directory + "new.pdf", FSDK.PDFDoc.e_SaveFlagNormal);

  let new_doc = comparison.GenerateComparedDoc(FSDK.Comparison.e_CompareTypeAll);
  new_doc.SaveAs(output_directory + "generate_result.pdf", FSDK.PDFDoc.e_SaveFlagNormal);

  console.log("Pdf compare test.");
} catch (e) {
  console.log(e);
}
FSDK.Library.Release();