Page 1 of 1

Set private data based on Pitstop Server log

Posted: Mon Jan 27, 2025 10:40 am
by JimmyHartington
In a flow I need to change the color of a mark on the pdf based on which colors are used.
So if the job contains Cyan the mark needs to be Cyan.
If it does not contain Cyan but contains Magenta the mark needs to Magenta.
Otherwise it needs to be Black.

My thought was to have Pitstop Server report the ink usage to me.
Then use a script expression to analyse the XML-log.

But I am a bit unsure how to read the XML dataset in a script expression.

I hope someone can point me in the right direction. :D

The logic for choosing the color seems pretty straight forward.

Code: Select all

  if (inkNames.includes("Cyan")) {
    return "Cyan";
  } else if (inkNames.includes("Magenta")) {
    return "Magenta";
  } else {
    return "Black";
  }
This is the XML I get returned from Pitstop Server.

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<EnfocusReport version="3.0" unit="mm" xml:lang="en-US">
  <PreflightReport errors="0" criticalfailures="0" noncriticalfailures="0" signoffs="0" fixes="0" warnings="0" informations="1">
    <Informations>
      <PreflightReportItem ActionID="3159">
        <Message>Document uses 4 separations, should be equal to 0. Those 4 separations are: Black, Cyan, Magenta, Yellow</Message>
        <StringContext>
          <BaseString>Document uses %NumberOfSeparations% separations, should be %Comparator% %ReferenceNumberOfSpotColors%. Those %NumberOfSeparations% separations are: %SeparationNames%</BaseString>
          <Var name="ReferenceNumberOfSpotColors">0</Var>
          <Var name="Comparator">equal to</Var>
          <Var name="NumberOfSeparations">4</Var>
          <Var name="SeparationNames">Black</Var>
          <Var name="SeparationNames">Cyan</Var>
          <Var name="SeparationNames">Magenta</Var>
          <Var name="SeparationNames">Yellow</Var>
        </StringContext>
      </PreflightReportItem>
    </Informations>
  </PreflightReport>
  <ProcessInfo>
    <PageRange>1</PageRange>
    <HostAppName>Enfocus PitStop Server 24.11</HostAppName>
    <EngineFlavor>Enfocus PitStop Library 24.11</EngineFlavor>
    <PreflightDateTime>2025-01-24T14:29:54+01:00</PreflightDateTime>
  </ProcessInfo>
  <GeneralDocInfo>
    <DocumentProperties>
      <DocumentName>_0P4LA_89215590.pdf</DocumentName>
      <NumPages>1</NumPages>
      <PDFVersion major="1" minor="6">1.6</PDFVersion>
      <CreationDate>2024-10-10T05:44:03+02:00</CreationDate>
      <ModificationDate>2024-10-10T05:44:03+02:00</ModificationDate>
      <Producer>Adobe PDF Library 17.0</Producer>
      <Creator>Adobe InDesign 19.5 (Macintosh)</Creator>
      <Author></Author>
      <Title></Title>
      <Subject></Subject>
      <Keywords></Keywords>
      <Trapped>false</Trapped>
      <PrinergyTraps>false</PrinergyTraps>
      <WasRepairedOnOpen>false</WasRepairedOnOpen>
      <IsLinearized>true</IsLinearized>
      <ContainsThumbnails>false</ContainsThumbnails>
      <LeftToRightReading>left</LeftToRightReading>
      <ContainsJobTicket>false</ContainsJobTicket>
    </DocumentProperties>
    <DocumentSecurity>
      <EncryptionType>none</EncryptionType>
      <Permissions>
        <ExtractAccessibility>true</ExtractAccessibility>
        <ExtractNonAccessibility>true</ExtractNonAccessibility>
        <ModifyNotes>true</ModifyNotes>
        <ModifyFillAndSign>true</ModifyFillAndSign>
        <ModifyAssembleDoc>true</ModifyAssembleDoc>
        <ModifyOther>true</ModifyOther>
        <ModifySecurity>true</ModifySecurity>
        <PrintLowQuality>true</PrintLowQuality>
        <PrintHighQuality>true</PrintHighQuality>
      </Permissions>
    </DocumentSecurity>
    <DocumentCompression>
      <DataFormat>binary</DataFormat>
      <IsCompressed>true</IsCompressed>
      <FilterType>ZIP</FilterType>
    </DocumentCompression>
  </GeneralDocInfo>
  <ColorInfo>
    <ColorSpace type="ICCBased" id="CS1" isValid="true">
      <ICCName>sRGB IEC61966-2.1</ICCName>
      <ICCSpace>DeviceRGB</ICCSpace>
      <ICCProfileID>1d3fda2edb4a89ab60a23c5f7c7d81dd</ICCProfileID>
      <Ranges>
        <Range name="R" min="0" max="1"/>
        <Range name="G" min="0" max="1"/>
        <Range name="B" min="0" max="1"/>
      </Ranges>
      <Location page="1" minX="0" minY="6.125" maxX="4.985" maxY="15.655"/>
    </ColorSpace>
  </ColorInfo>
  <PageColorTypeInfo emptyPages="0" blackAndWhitePages="0" colorPages="1">
    <Page index="1" colorType="color"/>
  </PageColorTypeInfo>
  <InkInfo>
    <DocumentInkInfo>
      <PaperArea>1100.16</PaperArea>
      <InkArea>147.58</InkArea>
      <InkPercentage>13.41</InkPercentage>
      <Ink name="Cyan">
        <Area>24.32</Area>
        <Percentage>2.21</Percentage>
      </Ink>
      <Ink name="Yellow">
        <Area>1.57</Area>
        <Percentage>0.14</Percentage>
      </Ink>
      <Ink name="Black">
        <Area>121.68</Area>
        <Percentage>11.06</Percentage>
      </Ink>
    </DocumentInkInfo>
    <PageInkInfo page="1">
      <PaperArea>1100.16</PaperArea>
      <InkArea>147.58</InkArea>
      <InkPercentage>13.41</InkPercentage>
      <Ink name="Cyan">
        <Area>24.32</Area>
        <Percentage>2.21</Percentage>
      </Ink>
      <Ink name="Yellow">
        <Area>1.57</Area>
        <Percentage>0.14</Percentage>
      </Ink>
      <Ink name="Black">
        <Area>121.68</Area>
        <Percentage>11.06</Percentage>
      </Ink>
    </PageInkInfo>
  </InkInfo>
</EnfocusReport>

Re: Set private data based on Pitstop Server log

Posted: Mon Jan 27, 2025 1:08 pm
by mkayyyy
Ended up taking me awhile to figure this one out, but reading the XML data set from a script expression looks like this:

Code: Select all

async function calculateScriptExpression(s: Switch, flowElement: FlowElement, job: Job): Promise<string> {
    const datasetPath = await job.getDataset("TestDataset", AccessLevel.ReadOnly);
    
    const xmlDocument = XmlDocument.open(datasetPath);
    const nsMap = xmlDocument.getDefaultNSMap();
    const inkNames = xmlDocument.evaluate("string(//PreflightReport/Informations/PreflightReportItem[@ActionID='3159']/Message)", nsMap) as string;

    await job.log(LogLevel.Info, "Ink Names: %1", [JSON.stringify(inkNames)]);

    if (inkNames.includes("Cyan")) {
        return "Cyan";
    } else if (inkNames.includes("Magenta")) {
        return "Magenta";
    } else {
        return "Black";
    }
}
The main bit that had me stuck for awhile is that you need to wrap the Xpath expression (in this case) with a string() function to get the value to be returned

Re: Set private data based on Pitstop Server log

Posted: Mon Jan 27, 2025 3:21 pm
by JimmyHartington
Thanks for this complete answer.

Your code looks at on part of the XML, but what I need to analyze is the section with "DocumentInkInfo".

Is it easy to change the path to look there?

Image

Re: Set private data based on Pitstop Server log

Posted: Mon Jan 27, 2025 4:10 pm
by mkayyyy
No worries, updating it to look at multiple nodes ends up getting a little bit janky due to the Node.js scripting engine. The scripting engine only supports Xpath expressions evaluating to boolean | number | string | undefined, so I don't think its possibly currently to get all the node values in one Xpath expression.

So to make it work for your use case of multiple nodes I made a version of the getXpath function from the Node.js Scripting docs that uses TS generics and called it twice checking if 'Cyan' or 'Magenta' is contained in the returned Xpath expression:

Code: Select all

async function calculateScriptExpression(s: Switch, flowElement: FlowElement, job: Job): Promise<string> {
    const datasetName = "TestDataset";
    const baseXPathExpression = "//DocumentInkInfo/Ink/@name";

    const containsCyan = await getXPath<boolean>(job, datasetName, `contains(${baseXPathExpression}, 'Cyan')`);
    const containsMagenta = await getXPath<boolean>(job, datasetName, `contains(${baseXPathExpression}, 'Magenta')`);

    if (containsCyan) {
        return "Cyan";
    } else if (containsMagenta) {
        return "Magenta";
    } else {
        return "Black";
    }
}

async function getXPath<T extends string | number | boolean>(job: Job, datasetName: string, xpath: string): Promise<T | null> {
    let result: string | number | boolean;

    try {
        const datasetPath = await job.getDataset(datasetName, AccessLevel.ReadOnly);

        const xmlDocument = XmlDocument.open(datasetPath);
        const nsMap = xmlDocument.getDefaultNSMap();
        result = xmlDocument.evaluate(xpath, nsMap) as T;
        
        await job.log(LogLevel.Info, "%1: %2", [xpath, (result as string)]);
    } catch (error: any) {
        await job.log(LogLevel.Error, "Error: %2", [(error as Error).message]);
        return null;
    }

    return result as T;
}

Re: Set private data based on Pitstop Server log

Posted: Mon Jan 27, 2025 6:15 pm
by JimmyHartington
Thanks, Matthew.

I will test it in my flow tomorrow.

Re: Set private data based on Pitstop Server log

Posted: Tue Jan 28, 2025 11:09 am
by JimmyHartington
It works as I expect.
Just have to process the pdf to have it only report inks from the trim box first, since it is this info I need.

Thank you very much for the help.

Re: Set private data based on Pitstop Server log

Posted: Tue Jan 28, 2025 11:16 am
by mkayyyy
Awesome, glad it works as you wanted

Re: Set private data based on Pitstop Server log

Posted: Wed Jan 29, 2025 4:46 pm
by freddyp
Matthew's solution is of course perfect and for the flow design easier than what I will suggest, but here is an approach that does not use scripting for those users that do not have the scripting module.

Define two pieces of private data on a folder after having run PitStop using the following XPath expression in Metadata:
count(//DocumentInkInfo/Ink[@name="Cyan"]) and one with Magenta of course.

You then test the private data key names that you defined for cyan and magenta being 1 or 0.

Re: Set private data based on Pitstop Server log

Posted: Thu Jan 30, 2025 7:50 am
by JimmyHartington
Thanks Freddy.
I keep forgetting you can count with the XPath expressions.

Re: Set private data based on Pitstop Server log

Posted: Thu Jan 30, 2025 4:21 pm
by mkayyyy
freddyp wrote: Wed Jan 29, 2025 4:46 pm Matthew's solution is of course perfect and for the flow design easier than what I will suggest, but here is an approach that does not use scripting for those users that do not have the scripting module.

Define two pieces of private data on a folder after having run PitStop using the following XPath expression in Metadata:
count(//DocumentInkInfo/Ink[@name="Cyan"]) and one with Magenta of course.

You then test the private data key names that you defined for cyan and magenta being 1 or 0.
:idea: Totally forgot about using XPath Metadata, also a good shout