-2

In Adobe InDesign, a magazine can have 64 pages or, sometimes, 120 pages, in general, a varying number of pages. It would be great if the script could indicate the total number of pages in the PDF document. For example, if the document has 120 pages, the script would automatically change the value of the page count. Additionally, the script should write the page count in words, for instance, "one hundred twenty" (if it's possible to automate this process). Otherwise, the word representation is not necessary. As a result, the script would create a new page with the caption "The current magazine is stitched, numbered, and sealed: 64 (sixty-four) pages" - this is just an example.If this approach is not feasible, we can try an alternative approach: We create templates with text for 20, 48, 64, 100, 200, and 300 pages. The script calculates the number of pages in the file and creates another page with the embedded template based on the page count. If the page count is not among the specified numbers, then a page with an empty template is created.

The script has returned various errors, for instance, error 24. I have tried many different script variations, but the result has always been unsuccessful. Below, you can find one of such scripts.

var myPDFFilePath = "/way/to/pdf.pdf";
var myPDFFile = File(myPDFFilePath);
var myPDFPageCount = myPDFFile.length;
var myDocument = app.activeDocument;
var lastPage = myDocument.pages[-1];
var myTextFrame = lastPage.textFrames.add();
myTextFrame.geometricBounds = lastPage.bounds;
function updateLastPageText(pageCount) {
  var myParagraph = myTextFrame.parentStory.paragraphs.add();
  myParagraph.contents = "The current magazine is stitched, numbered, and sealed:" + pageCount + "pages";
  if (myTextFrame.paragraphs.length > 1) {
    for (var i = myTextFrame.paragraphs.length - 2; i >= 0; i--) {
      myTextFrame.paragraphs[i].remove();
    }
  }
}
updateLastPageText(myPDFPageCount);
 
Yuri Khristich
  • 13,448
  • 2
  • 8
  • 23
Rayan
  • 1

2 Answers2

0

There are the script MultiPageImporter

https://github.com/mike-edel/ID-MultiPageImporter

And it has the functions to count pages of a PDF file. It's a tricky task. I took these functions from there.

So in your case the script could be something like this:

var pdf = File('d:/temp/test.pdf'); // your PDF
var info = getPDFInfo(pdf, false);  // get the info via Mike's function
var pgCount = info.pgCount;

var doc = app.activeDocument;           // current document
var lastPage = doc.pages.lastItem();    // last page

// add the text frame and fill it with the info
var myTextFrame = lastPage.textFrames.add();
myTextFrame.geometricBounds = lastPage.bounds;
myTextFrame.contents = 
    "The current magazine is stitched, numbered, and sealed: "
    +  pgCount + " pages";

// --------------------------------------------------------------
// from https://github.com/mike-edel/ID-MultiPageImporter

/******************************************/
/*        PDF READER SECTION              */
/*  Extracts count and size of pages      */
/******************************************/

// Extract info from the PDF file.
// getSize is a boolean that will also determine page size and rotation of first page
// *** File position changes in this function. ***
// Results are as follows:
// page count = retArray.pgCount
// page width = retArray.pgSize.pgWidth
// page height = retArray.pgSize.pgHeight
function getPDFInfo(theFile, getSize)
{
    var flag = 0; // used to keep track if the %EOF line was encountered

    // The array to hold return values
    var retArray = new Array();
    retArray["pgCount"] = -1;
    retArray["pgSize"] = null;

    // Open the PDF file for reading
    theFile.open("r");

    // Search for %EOF line
    // This skips any garbage at the end of the file
    // if FOE% is encountered (%EOF read backwards), flag will be 15
    for(i=0; flag != 15; i++)
    {
        theFile.seek(i,2);
        switch(theFile.readch())
        {
            case "F":
                flag|=1;
                break;
            case "O":
                flag|=2;
                break;
            case "E":
                flag|=4;
                break;
            case "%":
                flag|=8;
                break;
            default:
                flag=0;
                break;
        }
    }
    // Jump back a small distance to allow going forward more easily
    theFile.seek(theFile.tell()-100);

    // Read until startxref section is reached
    while(theFile.readln() != "startxref");

    // Set the position of the first xref section
    var xrefPos = parseInt(theFile.readln(), 10);

    // The array for all the xref sections
    var xrefArray = new Array();

    // Go to the xref section
    theFile.seek(xrefPos);

    // Determine length of xref entries
    // (not all PDFs are compliant with the requirement of 20 char/entry)
    xrefArray["lineLen"] = determineLineLen(theFile);

    // Get all the xref sections
    while(xrefPos != -1)
    {
        // Go to next section
        theFile.seek(xrefPos);

        // Make sure it's an xref line we went to, otherwise PDF is no good
        if (theFile.readln() != "xref")
        {
            throwError("Cannot determine page count.", true, 99, theFile);
        }

        // Add the current xref section into the main array
        xrefArray[xrefArray.length] = makeXrefEntry(theFile, xrefArray.lineLen);

        // See if there are any more xref sections
        xrefPos = xrefArray[xrefArray.length-1].prevXref;
    }

    // Go get the location of the /Catalog section (the /Root obj)
    var objRef = -1;
    for(i=0; i < xrefArray.length; i++)
    {
        objRef = xrefArray[i].rootObj;
        if(objRef != -1)
        {
            i = xrefArray.length;
        }
    }

    // Double check root obj was found
    if(objRef == -1)
    {
        throwError("Unable to find Root object.", true, 98, theFile);
    }

    // Get the offset of the root section and set file position to it
    var theOffset = getByteOffset(objRef, xrefArray);
    theFile.seek(theOffset);

    // Determine the obj where the first page is located
    objRef = getRootPageNode(theFile);

    // Get the offset where the root page nod is located and set the file position to it
    theOffset = getByteOffset(objRef, xrefArray);
    theFile.seek(theOffset);

    // Get the page count info from the root page tree node section
    retArray.pgCount = readPageCount(theFile);

    // Does user need size also? If so, get size info
    if(getSize)
    {
        // Go back to root page tree node
        theFile.seek(theOffset);

        // Flag to tell if page tree root was visited already
        var rootFlag = false;

        // Loop until an actual page obj is found (page tree leaf)
        do
        {
            var getOut = true;

            if(rootFlag)
            {
                // Try to find the line with the /Kids entry
                // Also look for instance when MediBox is in the root obj
                do
                {
                    var tempLine = theFile.readln();
                }while(tempLine.indexOf("/Kids") == -1 && tempLine.indexOf(">>") == -1);

            }
            else
            {
                // Try to first find the line with the /MediaBox entry
                rootFlag = true; // Indicate root page tree was visited
                getOut = false; // Force loop if /MediaBox isn't found here
                do
                {
                    var tempLine = theFile.readln();
                    if(tempLine.indexOf("/MediaBox") != -1)
                    {
                        getOut = true;
                        break;
                    }
                }while(tempLine.indexOf(">>") == -1);

                if(!getOut)
                {
                    // Reset the file pointer to the beginning of the root obj again
                    theFile.seek(theOffset)
                }
            }

            // If /Kids entry was found, still at an internal page tree node
            if(tempLine.indexOf("/Kids") != -1)
            {
                // Check if the array is on the same line
                if(tempLine.indexOf("R") != -1)
                {
                    // Grab the obj ref for the first page
                    objRef = parseInt(tempLine.split("/Kids")[1].split("[")[1]);
                }
                else
                {
                    // Go down one line
                    tempLine = theFile.readln();

                    // Check if the opening bracket is on this line
                    if(tempLine.indexOf("[") != -1)
                    {
                        // Grab the obj ref for the first page
                        objRef = parseInt(tempLine.split("[")[1]);
                    }
                    else
                    {
                        // Grab the obj ref for the first page
                        objRef = parseInt(tempLine);
                    }

                }

                // Get the file offset for the page obj and set file pos to it
                theOffset = getByteOffset(objRef, xrefArray);
                theFile.seek(theOffset);
                getOut = false;
            }
        }while(!getOut);

        // Make sure file position is correct if finally at a leaf
        theFile.seek(theOffset);

        // Go get the page sizes
        retArray.pgSize = getPageSize(theFile);
    }

    // Close the PDF file, finally all done!
    theFile.close();

    return retArray;

// internal functions ---------------------------------------------

// Function to create an array of xref info
// File position must be set to second line of xref section
// *** File position changes in this function. ***
function makeXrefEntry(theFile, lineLen)
{
    var newEntry = new Array();
    newEntry["theSects"] = new Array();
    var tempLine = theFile.readln();

    // Save info
    newEntry.theSects[0] = makeXrefSection(tempLine, theFile.tell());

    // Try to get to trailer line
    var xrefSec = newEntry.theSects[newEntry.theSects.length-1].refPos;
    var numObjs = newEntry.theSects[newEntry.theSects.length-1].numObjs;
    do
    {
        var getOut = true;
        for(i=0; i<numObjs;i++)
        {
            theFile.readln(); // get past the objects: tell( ) method is all screwed up in CS4
        }
        tempLine = theFile.readln();
        if(tempLine.indexOf("trailer") == -1)
        {
            // Found another xref section, create an entry for it
            var tempArray = makeXrefSection(tempLine, theFile.tell());
            newEntry.theSects[newEntry.theSects.length] = tempArray;
            xrefSec = tempArray.refPos;
            numObjs = tempArray.numObjs;
            getOut = false;
        }
    }while(!getOut);

    // Read line with trailer dict info in it
    // Need to get /Root object ref
    newEntry["rootObj"] = -1;
    newEntry["prevXref"] = -1;
    do
    {
        tempLine = theFile.readln();
        if(tempLine.indexOf("/Root") != -1)
        {
            // Extract the obj location where the root of the page tree is located:
            newEntry.rootObj = parseInt(tempLine.substring(tempLine.indexOf("/Root") + 5), 10);
        }
        if(tempLine.indexOf("/Prev") != -1)
        {
            newEntry.prevXref = parseInt(tempLine.substring(tempLine.indexOf("/Prev") + 5), 10);
        }

    }while(tempLine.indexOf(">>") == -1);

    return newEntry;
}

// Function to save xref info to a given array
function makeXrefSection(theLine, thePos)
{
    var tempArray = new Array();
    var temp = theLine.split(" ");
    tempArray["startObj"] = parseInt(temp[0], 10);
    tempArray["numObjs"] = parseInt(temp[1], 10);
    tempArray["refPos"] = thePos;
    return tempArray;
}

// Function that gets the page count form a root page section
// *** File position changes in this function. ***
function readPageCount(theFile)
{
    // Read in first line of section
    var theLine = theFile.readln();

    // Locate the line containing the /Count entry
    while(theLine.indexOf("/Count") == -1)
    {
        theLine = theFile.readln();
    }

    // Extract the page count
    return parseInt(theLine.substring(theLine.indexOf("/Count") +6), 10);
}

// Function to determine length of xref entries
// Not all PDFs conform to the 20 char/entry requirement
// *** File position changes in this function. ***
function determineLineLen(theFile)
{
    // Skip xref line
    theFile.readln();
    var lineLen = -1;

    // Loop trying to find lineLen
    do
    {
        var getOut = true;
         var tempLine = theFile.readln();
        if(tempLine != "trailer")
        {
            // Get the number of object enteries in this section
            var numObj = parseInt(tempLine.split(" ")[1]);

            // If there is more than one entry in this section, use them to determime lineLen
            if(numObj > 1)
            {
                theFile.readln();
                var tempPos = theFile.tell();
                theFile.readln();
                lineLen = theFile.tell() - tempPos;
            }
            else
            {
                if(numObj == 1)
                {
                    // Skip the single entry
                    theFile.readln();
                }
                getOut = false;
            }
        }
        else
        {
            // Read next line(s) and extract previous xref section
            var getOut = false;
            do
            {
                tempLine = theFile.readln();
                if(tempLine.indexOf("/Prev") != -1)
                {
                    theFile.seek(parseInt(tempLine.substring(tempLine.indexOf("/Prev") + 5)));
                    getOut = true;
                }
            }while(tempLine.indexOf(">>") == -1 && !getOut);
            theFile.readln(); // Skip the xref line
            getOut = false;
        }
    }while(!getOut);

    // Check if there was a problem determining the line length
    if(lineLen == -1)
    {
        throwError("Unable to determine xref dictionary line length.", true, 97, theFile);
    }

    return lineLen;
}

// Function that determines the byte offset of an object number
// Searches the built array of xref sections and reads the offset for theObj
// *** File position changes in this function. ***
function getByteOffset(theObj, xrefArray)
{
    var theOffset = -1;

    // Look for the theObj in all sections found previously
    for(i = 0; i < xrefArray.length; i++)
    {
        var tempArray = xrefArray[i];
        for(j=0; j < tempArray.theSects.length; j++)
        {
             var tempArray2 = tempArray.theSects[j];

            // See if theObj falls within this section
            if(tempArray2.startObj <= theObj && theObj <= tempArray2.startObj + tempArray2.numObjs -1)
            {
                theFile.seek((tempArray2.refPos + ((theObj - tempArray2.startObj) * xrefArray.lineLen)));

                // Get the location of the obj
                var tempLine = theFile.readln();

                // Check if this is an old obj, if so ignore it
                // An xref entry with n is live, with f is not
                if(tempLine.indexOf("n") != -1)
                {
                    theOffset = parseInt(tempLine, 10);

                    // Cleanly get out of both loops
                    j = tempArray.theSects.length;
                    i = xrefArray.length;
                }
            }
        }
    }

    return theOffset;
}

// Function to extract the root page node object from a section
// File position must be at the start of the root page node
// *** File position changes in this function. ***
function getRootPageNode(theFile)
{
    var tempLine = theFile.readln();

    // Go to line with /Page token in it
    while(tempLine.indexOf("/Pages") == -1)
    {
        tempLine = theFile.readln();
    }

    // Extract the root page obj number
    return parseInt(tempLine.substring(tempLine.indexOf("/Pages") + 6), 10);
}

}

The visual part of the result is not exactly impressive but it does the job:

enter image description here

To convert a number into words you can use this function:

// Based on:
// https://dev.to/slimpython/convert-number-to-words-in-javascript-with-explanation-30gj

function convertNumberToWords(number) {

    const numbersToWords = {
        0:  "zero",    1:  "one",     2:  "two",       3:  "three",    4:  "four",
        5:  "five",    6:  "six",     7:  "seven",     8:  "eight",    9:  "nine", 
        10: "ten",     11: "eleven",  12: "twelve",    13: "thirteen", 14: "fourteen",
        15: "fifteen", 16: "sixteen", 17: "seventeen", 18: "eighteen", 19: "nineteen", 
        20: "twenty",  30: "thirty",  40: "forty",     50: "fifty",    60: "sixty",   
        70: "seventy", 80: "eighty",  90: "ninety",
    };
    
    if (number in numbersToWords) return numbersToWords[number];
    var words = "";
    if (number >= 100) {
        words += convertNumberToWords(Math.floor(number / 100)) + " hundred";
        number %= 100;
    }
    if (number > 0) {
        if (words !== "") words += " and ";
        if (number < 20) words += numbersToWords[number];
        else {
            words += numbersToWords[Math.floor(number / 10) * 10];
            if (number % 10 > 0) {
                words += "-" + numbersToWords[number % 10];
            }
        }
    }
    return words;
}

alert(convertNumberToWords(123)); // --> one hundred and twenty-three

As for the rest (templates, etc) I'm not sure what it should look like. If you provide examples what you trying to get, I could try to improve the script.

Yuri Khristich
  • 13,448
  • 2
  • 8
  • 23
  • 1
    Okay. Let's hope it's just a plain classic Acrobat PDF file. Life it too much complicated these days. ) – Yuri Khristich Jul 17 '23 at 16:05
  • This is a regular PDF file. I moved the script to this path "....\Scripts\Scripts Panel\", but when I tried to activate it in Adobe InDesign, the application froze and didn't unfreeze even after 1-2 hours. Do I need to change the file extension to "jsx"? Also, I heard that I might need to install the "ID-MultiPageImporter" library for this script to work. Is that true? Could that be the reason why my application froze? – Rayan Jul 18 '23 at 12:23
  • The script should have .jsx extension, yes. It does not need to install MutliPageImporter. And make sure you have a correct path to your PDF file. In my example I wrote my test path, change it with your path and file name (the first line of the script). – Yuri Khristich Jul 18 '23 at 13:09
  • Now I will try the script again with jsx extension. Am I correct in understanding that the script creates a page with the page count in Adobe InDesign, not in the PDF? – Rayan Jul 18 '23 at 13:52
  • The thing is, we obtain the PDF file from the InDesign program. It is necessary for the script to work within InDesign itself. – Rayan Jul 18 '23 at 14:21
  • Of course it's a script for InDesign. It works within InDesign. It adds on the last page of current indd document the info about a PDF file. If you mean a script for Acrobat (to add a page into given PDF) you need another approach. But I'm not ready to provide an example of Acrobat script, though. – Yuri Khristich Jul 18 '23 at 16:43
  • Yes, I understand about that. But can this script be modified to work with an indd document instead of a PDF? – Rayan Jul 19 '23 at 13:50
  • Do you mean to count pages of current indd document? But it's not a problem at all. `var page_counter = app.activeDocument.pages.length` – Yuri Khristich Jul 19 '23 at 14:55
0

If you mean to get a number of pages of current indd document it can be done much easier:

var doc = app.activeDocument;     // current document
var page = doc.pages.lastItem();  // the last page
var counter = doc.pages.length;

// remove all objects from the last page
while(page.allPageItems.length) page.allPageItems[0].remove();

// add the text frame and fill it with the info
var frame = page.textFrames.add();
frame.geometricBounds = page.bounds;
frame.contents = "The current magazine is stitched, numbered, and sealed: " + counter + " pages";

// set the vertical alignment for the text frame
frame.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;

And you can convert the number into words with the function convertNumberToWords() from my another answer.

var counter = convertNumberToWords(doc.pages.length);

To justify the text vertically by center:

frame.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;
Yuri Khristich
  • 13,448
  • 2
  • 8
  • 23
  • The script works great! Actually, I modified the number of pages in words notification into a function so that it is also displayed in the result. Now, how can I move this result to the center of the page (currently it is displayed at the top)? I mean, not centering it horizontally, but vertically in the middle of the page. – Rayan Jul 20 '23 at 20:41
  • Add at the end of the snippet this line `frame.textFramePreferences.verticalJustification = VerticalJustification.CENTER_ALIGN;` – Yuri Khristich Jul 20 '23 at 21:49
  • Last question: Is it possible for the script to modify the content of the last page, leaving only what is necessary ("In the current magazine...")? Let's say there was some information on the page, let's say "123". The script removes this information ("123") and adds the output of its work (page count) on the same page. – Rayan Jul 21 '23 at 18:17
  • It depends what exactly looks like the 'information'. Is it a part of some text or some text frames, etc. To remove all page items from a given `page` you can use the line like this: `while(page.allPageItems.length) page.allPageItems[0].remove();` – Yuri Khristich Jul 21 '23 at 19:03
  • I inserted this line after 'frame.geometricBounds = page.bounds;', but now the script's result is a completely blank page. I need the script to remove the previous information on the page and add its result of the page count – Rayan Jul 30 '23 at 13:41
  • You have to insert the new line before the line `var frame = page.textFrames.add();` It's quite obvious. Since the new line removes everything on the page. It is it works fine, please consider to accept the answer. – Yuri Khristich Jul 31 '23 at 19:06
  • Now, it's showing error 30476 ("The requested operation cannot be completed because the object no longer exists.") at line 6 (frame.geometricBounds = page.bounds;) – Rayan Aug 02 '23 at 09:22
  • Please show your final code. – Yuri Khristich Aug 02 '23 at 13:09
  • https://prnt.sc/xgUDkaVGkGr9 – Rayan Aug 02 '23 at 16:48
  • Reread my last comment. The line `while(page.allPageItems.length) page.allPageItems[0].remove();` should be **BEFORE** the line `var frame = page.textFrames.add();`. Or you can just copy this part of the code from my answer. – Yuri Khristich Aug 03 '23 at 18:44