/*global xfa designer */ // form report tool. Written by John Brinkman var Summary = [], vTemplateString, aReport, vRootSubform, vDesc, vEmbeddedImageCount, reportContent, vRegex, vTemplateVersion, vOriginalVersion; // Display a piece of summary information function displayInfo(vName, vValue){ Summary.push({name: vName, value: vValue}); } // replace special characters so content can be treated as pcdata function escapeChars(start) { if (! start.length) { return start; } return start.replace(/&/g, "&").replace(/ var vRegexp = /font-family:[']*([^;'"]*)/g; // loop through each occurence of the "font-family:" string. while ((result = vRegexp.exec(xhtml)) !== null) { increment(reportContent.fonts, result[1]); } } // Loop through all the elements in the template and collect // interesting information to report later. function generateReport(vObj) { var vName, vContainer, vContext, i, vLines, vCount; if (vObj.isContainer) { // track how many field, subform, draw ... objects increment(reportContent.containers, vObj.className); // Track the binding properties of those containers // that can bind to data. switch (vObj.className) { case "exclGroup": case "field": case "subform": // Track all the binding types: once, dataRef, global, none increment(reportContent.bindings, vObj.bind.match); break; } } else { // increment the count of all property elements increment(reportContent.properties, vObj.className); // Some special cases of properties where we want extra reporting. // Track all the typeface names used in the template if (vObj.className === "font") { increment(reportContent.fonts, vObj.typeface); } // Find typeface references inside rich text fragments if (vObj.className === "#xHTML") { extractTypefaces(vObj); } // Track all linked and embedded images (and image sizes) if (vObj.className === "image") { if (vObj.value === vObj.href) { // a linked image // truncate the name for display vName = vObj.value; if (vName.length > 26) { vName = ".." + vName.substring(vName.length - 24); } increment(reportContent.images, vName); } else { // an embedded image // Generate a unique name so that we report one line per embedded image vName = reportContent.embeddedImageCount.toString() + ":embedded"; // For embedded images we normally store the name under the // field/draw element vContainer = vObj.parent; while (!vContainer.isContainer) { vContainer = vContainer.parent; } if (vContainer.desc.nodes.namedItem("embeddedHref") !== null) { vName = vContainer.desc.embeddedHref.value; if (vName.length > 23) { vName = ".." + vName.substring(vName.length - 21); } } increment(reportContent.images, reportContent.embeddedImageCount.toString() + ":" + vName, 1, vObj.value.length); reportContent.embeddedImageCount++; } } // Count how many times we use rich vs. plain text for field/draw content if (vObj.className === "exData"&& vObj.parent.className === "value") { if (vObj.contentType === "text/html") { increment(reportContent.textTypes, "xhtml"); } } if (vObj.className === "text" && vObj.parent.className === "value") { increment(reportContent.textTypes, "plain"); } // Report on all the different picture clauses used if (vObj.className === "picture") { increment(reportContent.pictures, vObj.value); } // Report on all usage of script if (vObj.className === "script") { vContext = vObj.parent.className; if (vContext === "event") { vContext = vObj.parent.activity; } if (vObj.contentType === "application/x-javascript") { vContext += " (JavaScript)"; } else { vContext += " (FormCalc)"; } // Count the lines of script (excluding empty lines) // Access the value property using ["#value"] in order to avoid // a potential conflict with a script object variable named "value". vLines = vObj["#value"].split("\n"); vCount = vLines.length; for (i = 0; i < vLines.length; i++) { if (vLines[i].length === 0) { vCount--; } } increment(reportContent.scripts, vContext, 1, vCount); } } // recurse into the child elements for (i = 0; i < vObj.nodes.length; i++) { generateReport(vObj.nodes.item(i)); } } // Generate a category report function report(vGroup) { // build an array of all the property names so that we can sort them var i, vClass, vReport = []; for (vClass in vGroup) { if (vGroup.hasOwnProperty(vClass)) { if (vClass[0] === "#") { vReport.push(vClass); } } } vReport = vReport.sort(); // Create a category subform and define the title and column headings aReport.push(""); aReport.push("" + vGroup.title + ""); for (i = 0; i < vGroup.headers.length; i++) { aReport.push("" + vGroup.headers[i] + ""); } // generate a row for each property instance for (i = 0; i < vReport.length; i++) { aReport.push("" + vReport[i].substring(1) + ""); aReport.push("" + vGroup[vReport[i]].count + ""); if (vGroup[vReport[i]].other > 0) { aReport.push("" + vGroup[vReport[i]].other + ""); } aReport.push(""); } aReport.push(""); } // Define the group object we use to track properties function Group(vTitle, vHeaders) { vHeaders = vHeaders || ["Name", "Count"]; this.title = vTitle; this.headers = vHeaders; } var i, msgno = 0; function trace(msg) { designer.println(msgno.toString() + " " + msg + "\r\n"); msgno++; } try { // Extract some details from processing instructions. // only way to do that is to find them in the XML... vTemplateString = xfa.template.saveXML(); // Get the XFA version number from the template namespace vRegex = /[\n|\r|\s|>]+]+<\?originalXFAVersion http:\/\/www.xfa.org\/schema\/xfa-template\/([0-9\.]*)/; vResult = vRegex.exec(vTemplateString); if (vResult !== null) { vOriginalVersion = parseFloat(vResult[1]); } displayInfo("Compatible Version", vOriginalVersion.toString()); // Find the state of strict scoping. // The rules are: // If original version is >= 2.8 then strict is ON by default. // If original version is < 2.8 then strict is OFF by default. // If original version is < 2.8 the strict is turned ON with the PI: // // If original version is >= 2.8 the strict is turned OFF with the PI: // var vStrictScoping = vOriginalVersion > 2.7 ? "ON" : "OFF"; if (vTemplateString.match(/[\n|\r|>]+<\?acrobat JavaScript strictScoping\?>/) !== null) { vStrictScoping = "ON"; } else { // As of 2.8, strict scoping is on by default // but can be turned off in the originalXFAVersion PI vRegex = /[\n|\r|>]+<\?originalXFAVersion.* v([0-9\.]*)\-scripting:1/; vResult = vRegex.exec(vTemplateString); if (vResult !== null) { if (parseFloat(vResult[1]) < 2.8) { vStrictScoping = "OFF"; } } } displayInfo("Strict Scoping", vStrictScoping); displayInfo("Embed Fonts", vTemplateString.match(/[\n|\r|>]+<\?templateDesigner SavePDFWithEmbeddedFonts 1\?>/) !== null); vTemplateString = null; // big string: free up for garbage collection. // Display any data found in the root subform element. vDesc = xfa.template["#subform"].desc; for (i = 0; i < vDesc.nodes.length; i++) { displayInfo(vDesc.nodes.item(i).name, vDesc.nodes.item(i).value); } // Set up objects that will track groups of properties reportContent = { properties: new Group("All Other Properties"), containers: new Group("Containers"), fonts: new Group("Fonts"), bindings: new Group("Binding Properties"), images: new Group("Images", ["Name", "Count", "Image Size"]), textTypes: new Group("Text Types"), pictures: new Group("Picture Formats"), scripts: new Group("Scripts", ["Name", "Count", "Lines of Script"]), embeddedImageCount: 0 }; // We make a copy of the template subform here. // If we don't make a copy, then the report will be influenced by "default elements" // that get created when the template is queried as it is opened. vRootSubform = xfa.template.createNode("subform"); vRootSubform.loadXML(xfa.template["#subform"].saveXML()); // Gather all the information in the template into the groups. generateReport(vRootSubform); // Generate the report. aReport = []; aReport.push(''); aReport.push(""); aReport.push(""); for (i = 0; i < Summary.length; i++) { aReport.push("" + Summary[i].name + ""); aReport.push("" + escapeChars(Summary[i].value) + ""); } aReport.push(""); report(reportContent.containers); report(reportContent.fonts); report(reportContent.images); report(reportContent.textTypes); report(reportContent.scripts); report(reportContent.bindings); report(reportContent.pictures); report(reportContent.properties); aReport.push(""); aReport.push(""); designer.showXDPinAcrobat(aReport.join("\n"), "FormReporter"); } catch (err) { if (typeof(err) === "string") { trace(err); } else { trace(err.message + "\r\nline: " + err.line.toString()); } }