// 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 create a PDF document,
// insert the text path and image objects into the created PDF document, copy shading objects,
// and save the file with inserted graphics objects.


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/";

class TextDoc {
  constructor(file_name, file_mode) {
    this.file_name = file_name;
    this.file_mode = file_mode;
    this.file_stream = fs.openSync(file_name, file_mode);
    if (!this.file_stream) {
      throw new Error('Cannot open file');
    }
  }
  Write(format, ...args) {
    if(!this.file_stream) 
      throw new Error("File not opened");
    let formatted_string = util.format(format, ...args);
    let buffer = new Buffer.from(formatted_string, 'utf-8');
    fs.writeSync(this.file_stream, buffer, 0, buffer.length);
  }
  WriteWithPrefix(count, prefix, format, ...args){
    for (let i = 0; i < count; i++) {
      this.Write(prefix);
    }
    this.write(format, ...args);
  }
  Close() {
    fs.closeSync(this.file_stream);
    this.file_stream = null;
  }
}


function AddTextObjects(page) {
  let position = page.GetLastGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeText);
  let text_object = FSDK.TextObject.Create();

  text_object.SetFillColor(0xFFFF7F00);

  // Prepare text state
  let state = new FSDK.TextState();
  state.font_size = 80.0;
  state.font =  new FSDK.Font("Simsun", FSDK.Font.e_StylesSmallCap, FSDK.Font.e_CharsetGB2312, 0);
  state.textmode = FSDK.TextState.e_ModeFill;
  text_object.SetTextState(page, state, false, 750);

  // Set text.
  text_object.SetText("Foxit Software");
  let last_position = page.InsertGraphicsObject(position, text_object);

  let rect = text_object.GetRect();
  let offset_x = (page.GetWidth() - (rect.right - rect.left)) / 2;
  let offset_y = page.GetHeight() * 0.8 - (rect.top - rect.bottom) / 2;
  text_object.Transform(new FSDK.Matrix2D(1, 0, 0, 1, offset_x, offset_y), false);

  // Generate content
  page.GenerateContent();

  // Clone a text object from the old text object.
  text_object = text_object.Clone().GetTextObject()
  

  state.font = new FSDK.Font(FSDK.Font.e_StdIDTimes);
  state.font_size = 48;
  state.textmode = FSDK.TextState.e_ModeFillStrokeClip;

  text_object.SetTextState(page, state, true, 750);
  text_object.SetText("www.foxitsoftware.com");

  text_object.SetFillColor(0xFFAAAAAA);
  text_object.SetStrokeColor(0xFFF68C21);
  page.InsertGraphicsObject(last_position, text_object);

  rect = text_object.GetRect();
  offset_x = (page.GetWidth() - (rect.right - rect.left)) / 2;
  offset_y = page.GetHeight() * 0.618 - (rect.top - rect.bottom) / 2;
  text_object.Transform(new FSDK.Matrix2D(1, 0, 0, 1, offset_x, offset_y), false);

  // Generate content again after transformation.
  page.GenerateContent();

  //Show how to get the characters' information of text object.
  let text_object_charcount = text_object.GetCharCount();
  let text_doc = new TextDoc(output_path + "graphics_objects/text_objects_info.txt", "w");
  let strinfo = `The new text object has ${text_object_charcount} characters.\n`;
  text_doc.Write(strinfo);

  for (let i = 0; i < text_object_charcount; i++) {
	//The character's position.
    let char_pos = text_object.GetCharPos(i);
    //The character's width.
    let width = text_object.GetCharWidthByIndex(i);
    //The character's height.
    let height = text_object.GetCharHeightByIndex(i);    
    let strinfo = `The position of the ${i} characters is (${char_pos.x}, ${char_pos.y}).The width and height of the characters is (${width}, ${height}).\n`;    
    text_doc.Write(strinfo);
  }
}

function AddImageObjects(page, image_file) {
  let position = page.GetLastGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeImage);
  let image = new FSDK.Image(image_file);
  let image_object = FSDK.ImageObject.Create(page.GetDocument());
  image_object.SetImage(image, 0);

  let width = image.GetWidth();
  let height = image.GetHeight();

  let page_width = page.GetWidth();
  let page_height = page.GetHeight();

  // Please notice the matrix value.
  image_object.SetMatrix(new FSDK.Matrix2D(width, 0, 0, height, (page_width - width) / 2.0, (page_height - height) / 2.0));

  page.InsertGraphicsObject(position, image_object);
  page.GenerateContent();
}

function AddPieces(page, orignal_pieces, dst_rect) {
  let position = page.GetFirstGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeAll);
  pieces = orignal_pieces.Clone();

  let piece_rect = pieces.GetRect();

  // Calculates the transformation matrix between dst_rect and  piece_rect.
  let a = (dst_rect.right - dst_rect.left) / (piece_rect.right - piece_rect.left);
  let d = (dst_rect.top - dst_rect.bottom) / (piece_rect.top - piece_rect.bottom);
  let e = dst_rect.left - piece_rect.left * a;
  let f = dst_rect.top - piece_rect.top * d;

  // Transform rect.
  pieces.Transform(new FSDK.Matrix2D(a, 0, 0, d, e, f), false);
  page.InsertGraphicsObject(position, pieces);

  page.GenerateContent();
}

function AddPathObjects(page, black_pieces, white_pieces) {
  let position = page.GetLastGraphicsObjectPosition(FSDK.GraphicsObject.e_TypePath);
  let path_object = FSDK.PathObject.Create();
  let path = new FSDK.Path();
  let page_width = page.GetWidth();
  let page_height = page.GetHeight();

  let width = Math.min(page_width, page_height) / 20.0;
  let start_x = (page_width - width * 18.0) / 2.0;
  let start_y = (page_height - width * 18.0) / 2.0;

  // Draw a chess board
  for (let i = 0; i < 19; i++) {
    let x1 = start_x;
    let y1 = i * width + start_y;

    let x2 = start_x + 18 * width;
    path.MoveTo(new FSDK.PointF(x1, y1));
    path.LineTo(new FSDK.PointF(x2, y1));

    x1 = i * width + start_x;
    y1 = start_y;

    let y2 = 18 * width + start_y;
    path.MoveTo(new FSDK.PointF(x1, y1));
    path.LineTo(new FSDK.PointF(x1, y2));
  }

  let star = [3, 9, 15 ];
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      let rect = new FSDK.RectF (start_x + star[i] * width - width / 12, start_y + star[j] * width - width / 12,
        start_x + star[i] * width + width / 12, start_y + star[j] * width + width / 12);
      path.AppendEllipse(rect);
    }
  }
  path_object.SetPathData(path);

  path_object.SetFillColor(0xFF000000);
  path_object.SetFillMode(FSDK.e_FillModeAlternate);
  path_object.SetStrokeState(true);
  path_object.SetStrokeColor(0xFF000000);

  page.InsertGraphicsObject(position, path_object);
  page.GenerateContent();

  // Draw pieces
  let pieces_vector = [
    [new FSDK.PointF(3, 3), new FSDK.PointF(3, 7),   new FSDK.PointF(3, 15),  new FSDK.PointF(13, 2),
    new FSDK.PointF(13, 16), new FSDK.PointF(13, 17), new FSDK.PointF(15, 16), new FSDK.PointF(16, 16)],
    [new FSDK.PointF(11, 16), new FSDK.PointF(12, 14), new FSDK.PointF(14, 4),  new FSDK.PointF(14, 15),
    new FSDK.PointF(15, 3),  new FSDK.PointF(15, 9),  new FSDK.PointF(15, 15), new FSDK.PointF(16, 15)]
    ];
  for (let k = 0; k < 2; k++) {
    for (let i = 0; i < pieces_vector[k].length; i++) {
      let x = pieces_vector[k][i].x;
      let y = pieces_vector[k][i].y;
      AddPieces(page, k % 2 ? white_pieces : black_pieces,
        new FSDK.RectF(start_x + x * width - width / 2.0, start_y + y * width - width / 2.0,
        start_x + x * width + width / 2.0, start_y + y * width + width / 2.0));
    }
  }
}

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 + "graphics_objects/";
if (!fs.existsSync(output_directory)) {
  fs.mkdirSync(output_directory);
}

let input_file = input_path + "graphics_objects.pdf";

try {
  let doc = new FSDK.PDFDoc(input_file);
  let error_code = doc.Load("");

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

  // Get original shading objects from PDF page.
  let original_page = doc.GetPage(0);
  original_page.StartParse(FSDK.PDFPage.e_ParsePageNormal, null, false);
  let position = original_page.GetFirstGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeShading);
  if(!position)
    return 1;
  let black_pieces = original_page.GetGraphicsObject(position);
  position = original_page.GetNextGraphicsObjectPosition(position, FSDK.GraphicsObject.e_TypeShading);
  let white_pieces = original_page.GetGraphicsObject(position);

  // Add a new PDF page and insert text objects.
  let page = doc.InsertPage(0, FSDK.PDFPage.e_SizeLetter);
  AddTextObjects(page);

  // Add a new PDF page and insert image objects.
  page = doc.InsertPage(1, FSDK.PDFPage.e_SizeLetter);
  let image_file = input_path + "sdk.png";
  AddImageObjects(page, image_file);

  // Add a new PDF page and insert path objects, and copy shading objects.
  page = doc.InsertPage(2, FSDK.PDFPage.e_SizeLetter);
  AddPathObjects(page, black_pieces, white_pieces);

  let output_file = output_directory + "graphics_objects.pdf";
  doc.SaveAs(output_file, FSDK.PDFDoc.e_SaveFlagNormal);
  console.log("Add graphics objects to PDF file.");

  // Remove text objects from PDF page.
  page = doc.GetPage(0);
  page.StartParse(FSDK.PDFPage.e_ParsePageNormal, null, false);
  position = page.GetFirstGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeAll);
  while (position)
  {
    let obj = page.GetGraphicsObject(position);
    if (obj.GetType() == FSDK.GraphicsObject.e_TypeText)
    {
      let textobj = obj.GetTextObject();
      let s = textobj.GetText();
      if (s != "")
      {
        page.RemoveGraphicsObject(textobj);
        position = page.GetFirstGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeAll);
        continue;
      }
    }
    else if (obj.GetType() == FDSK.GraphicsObject.e_TypeFormXObject)
    {
      let formxobj = obj.GetFormXObject();
      let graphicsObjects_form = formxobj.GetGraphicsObjects();
      let pos = graphicsObjects_form.GetFirstGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeText);
      if (pos)
      {
        let textobj = page.GetGraphicsObject(pos).GetTextObject();
        if (textobj.GetType() == FSDK.GraphicsObject.e_TypeText)
        {
          let s = textobj.GetText();
          if (s != "")
          {
            page.RemoveGraphicsObject(obj);
            position = page.GetFirstGraphicsObjectPosition(FSDK.GraphicsObject.e_TypeAll);
            continue;
          }
        }
      }
    }
    position = page.GetNextGraphicsObjectPosition(position, FSDK.GraphicsObject.e_TypeAll);
  }
  page.GenerateContent();

  output_file = output_directory + "graphics_objects_remove_text_objects.pdf";
  doc.SaveAs(output_file, FSDK.PDFDoc.e_SaveFlagNormal);
  console.log("Remove text objects from PDF file.");
} catch (e) {
  console.log(e)
}
FSDK.Library.Release();
