Jump to content

step

Registered Users - Approved
  • Posts

    962
  • Joined

Everything posted by step

  1. There are several way so to do this but the method that I think looks the cleanest is using an Array. If you put each of your paragraphs into an array, you can then filter it to remove the empty paragraphs, and then splice the first 4 populated paragraphs. One way to achieve this is using a table. Essentially each paragraph will have a row in the table. Each row will consist of two columns: the left column will contain the yellow square (an inline graphic) and the right column will contain the paragraph. Here's an example: var sq = CreateResource('./YellowSquare.pdf','graphic'); // Sqaure graphic var r = []; // Array to hold paragraphs // Populate array with 'Headline' and 'Body' fields for (var i=1; i<=8; i++) r.push([Field('Headline' + i), Field('Body' + i)]); // Filter out empty paragraphs and splice the first 4 r = r.filter(function(s){ return s[0] && s[1];}).splice(0,4); var table = new FPTable(); table.AddColumns(3600,14400); // Set your column widths here for (var i in r) table.AddRow().SetContents(sq.content, r[i].join('<br>')); return table.MakeTags();
  2. Oh, that's definitely useful! Thanks for the clarification, Dan. I will (finally) be switching over to FP9 next week so I won't have to keep hypothesizing about all of its features
  3. Okay, I think I understand what you're saying. That's basically what I was doing by opening a new output file at the start of a chunk or the first record in the data file. Okay, if you say so. I don't know what version of FusionPro you have installed but I'm running version 8 and as of version 8 the input file is accessed by FusionPro.Composition.inputFileName just as it's inserted by the "Building Blocks." I'm not saying that they couldn't have changed it in later versions of the software but that seems like something that would have disrupted existing templates that relied on that property. And you'll have to be a little more specific when you say it "returns null" or "returns the correct naming convention." Do you mean in validation or preview? Or are you actually composing the file to see if it's been named like you'd expect? The template can't return the name of the input file until the file is being composed so you'd need to compose the job to check the naming convention.
  4. I think you're going to want to convert the "chunkSize" variable to an integer using the "Int" function. What is the purpose of the "boolflag" variable? Are you including that as some sort of exit status for the function? If so, that's not necessary. If you're using it to determine whether the job is being chunked or not, as I said in my other post, you can determine that based on the "chunkSize" variable. The correct way to access the input file's name is FusionPro.Composition.inputFileName. FusionPro.inputFileName will not return anything and your "outputName2" will end in a trailing underscore (_) as a result. Using the "building blocks" will allow you to access the properties of the FusionPro object as well as give a description. From the Building Blocks, click Objects > FusionPro (Global) > Composition > Input and Output > inputFileName. Double-clicking on the "inputFileName" will insert it into your rule editor. I would consider changing your code to something like this for: var output = 'Job_#_' + Field('Filename'); var input = FusionPro.Composition.inputFileName; var chunkSize = Int(FusionPro.Composition.JobOptions["RecordsPerChunk"]); var beginfile = FusionPro.Composition.inputRecordNumber; var range = beginfile + (chunkSize-1); output += chunkSize ? '_' + beginfile + '_' + range : (input.match(/_ALL|_LS/) || [''])[0]; if (beginfile % chunkSize == 1 || beginfile == 1) FusionPro.Composition.OpenNewOutputFile(output + '.' + FusionPro.Composition.outputFormatExtension); Of course the above code just adds the chunk size to the end of the file name and it may not truly reflect what records are in the file. For example, if you have a 55 record data file set to chunk in sets of 10, your last file would be named "Job_#_FILENAME_51_60.pdf" when it should be named "Job_#_FILENAME_51_55.pdf". To handle that scenario, you'd have to force FusionPro to preprocess the job by putting this in an "OnJobStart" callback: FusionPro.Composition.forcePreprocessing = FusionPro.Composition.JobOptions["RecordsPerChunk"]; And your OnRecordStart rule would look like this: var output = 'Job_#_' + Field('Filename'); var input = FusionPro.Composition.inputFileName; var totalRecords = FusionPro.Composition.totalRecordCount; var chunkSize = Int(FusionPro.Composition.JobOptions["RecordsPerChunk"]); var beginfile = FusionPro.Composition.inputRecordNumber; var range = beginfile + (chunkSize-1); if (range >= totalRecords) range = totalRecords; output += chunkSize ? '_' + beginfile + '_' + range : (input.match(/_ALL|_LS/) || [''])[0]; if (beginfile % chunkSize == 1 || beginfile == 1) FusionPro.Composition.OpenNewOutputFile(output + '.' + FusionPro.Composition.outputFormatExtension);
  5. You can determine if the the multiple files box is checked like this: var isChunked = FusionPro.Composition.JobOptions['OutputSource'] == 'bychunk'; return isChunked; // returns true when "Output to multiple files" is selected The number of records in each chunk is accessed by the "RecordsPerChunk" property and since that property will not exist if "Output to multiple files" is not selected during composition, it can also be used to determine if a composition will be chunked: var recsInChunk = FusionPro.Composition.JobOptions['RecordsPerChunk']; if (recsInChunk) { // Do something if "Output to multiple files" is checked } As far as your code goes, it appears your aiming to accomplish something very similar to this thread.
  6. Basically the first part of the page name ("Prev" or "Pkt") is decided by whether or not the template is being previewed. The second part (the number) is decided by which design is being used so adding additional text ("Des") to that value is just making matters more complicated. It would be a little less messy if your pages were named "PrevDes1" or the values for your "Design" field were simply: "1" but that's just my opinion. As it stands, you can determine which page to turn on by name like this: var page = ['Pkt','Prev'][+IsPreview()] + Field("Design").replace('Des',''); FusionPro.Composition.SetBodyPageUsage(page, true); Or, you could determine which page needs to be turned on by page number in the template: var page = Int(Field("Design").replace('Des',''))*2 - IsPreview(); FusionPro.Composition.SetBodyPageUsage(page, true);
  7. Okay thanks that clears things up a bit. You're returning the tables from a global string (tbl) which you need to reset at the beginning of each record: //The following if statement will detect if we are currently in preview mode or editing this rule (versus composing output). if(FusionPro.Composition.isPreview == true || FusionPro.inValidation == true) { Rule("OnJobStart"); } // Create empty var ArraySize = 0; var tables = []; [color="Red"]tbl = '';[/color] //Get a count of the total number of records in the external data file NumberOfRecords = data.recordCount; //Now, loop through all records in the external data file and find the records that belong to the customer. for (var i = 1; i <= NumberOfRecords; i++) { if (data.GetFieldValue(i, "CID") == Field("Customer ID")) { //Create Table var myTable = new FPTable; myTable.AddColumns(5825, 5400, 27200, 7200, 8100); myTable.AddRows(15); myTable.Rows[0].Cells[3].HStraddle = 2; myTable.Rows[1].Cells[0].HStraddle = 2; myTable.Rows[1].Cells[2].VStraddle = 2; myTable.Rows[1].Cells[3].VStraddle = 2; myTable.Rows[1].Cells[4].VStraddle = 2; myTable.Rows[0].Cells[0].PointSize=10; myTable.Rows[1].Cells[0].PointSize=10; myTable.Rows[2].Cells[0].PointSize=8; myTable.Rows[3].Cells[0].PointSize=10; myTable.Rows[4].Cells[0].PointSize=10; myTable.Rows[5].Cells[0].PointSize=10; myTable.Rows[6].Cells[0].PointSize=10; myTable.Rows[7].Cells[0].PointSize=10; myTable.Rows[8].Cells[0].PointSize=10; myTable.Rows[9].Cells[0].PointSize=10; myTable.Rows[10].Cells[0].PointSize=10; myTable.Rows[11].Cells[0].PointSize=10; myTable.Rows[12].Cells[0].PointSize=10; myTable.Rows[13].Cells[0].PointSize=10; //Title Row myTable.Rows[0].SetContents("County", data.GetFieldValue(i, "County"), "Property ID", data.GetFieldValue(i, "PropertyID"), ""); //Header Rows myTable.Rows[1].Cells[0].Content = "Head Count"; myTable.Rows[1].Cells[2].Content = "Livestock Type"; myTable.Rows[1].Cells[3].Content = "Fee Amount Per Head"; myTable.Rows[1].Cells[4].Content = "Per Capita Fee Calculated Totals"; myTable.Rows[2].Cells[0].Content = "Previous Year"; myTable.Rows[2].Cells[1].Content = "Current Year"; //Variable Data Rows myTable.Rows[3].SetContents(data.GetFieldValue(i, "Horses"), "", "Horses, Mules and Asses (ponies, donkeys, burros)", "$ 5.85", ""); myTable.Rows[4].SetContents(data.GetFieldValue(i, "Cattle"), "", "Cattle (cows, bulls, yearling)", "$ 2.29", ""); myTable.Rows[5].SetContents(data.GetFieldValue(i, "Bison"), "", "Domestic Bison", "$ 6.38", ""); myTable.Rows[6].SetContents(data.GetFieldValue(i, "Sheep"), "", "Sheep", "$ 0.54", ""); myTable.Rows[7].SetContents(data.GetFieldValue(i, "Swine"), "", "Swine", "$ 0.78", ""); myTable.Rows[8].SetContents(data.GetFieldValue(i, "Goats"), "", "Goats", "$ 0.54", ""); myTable.Rows[9].SetContents(data.GetFieldValue(i, "Poultry"), "", "Poultry (chickens, turkeys, gees, ducks and other domestic birds raised as food or to produce feathers", "$ 0.05", ""); myTable.Rows[10].SetContents(data.GetFieldValue(i, "Bees"), "", "Bees (number of hives or boards)", "$ 0.41", ""); myTable.Rows[11].SetContents(data.GetFieldValue(i, "Domestic"), "", "Alternative Livestock (privately owned caribou, mule deer, whitetail deer, elk, moose, antelope, mountain sheep, mountain goats indigenous to Montana)", "$26.23", ""); myTable.Rows[12].SetContents(data.GetFieldValue(i, "Ratites"), "", "Ratites (includes all ostriches, rheas and emus", "$ 9.37", ""); myTable.Rows[13].SetContents(data.GetFieldValue(i, "Llamas"), "", "Llamas and Alpacas", "$ 9.37", ""); // Footer Row myTable.Rows[14].Cells[0].HStraddle = 4; myTable.Rows[14].Cells[4].HStraddle = 4; myTable.Rows[14].Cells[4].PointSize=10; myTable.Rows[14].Cells[0].Content = "Total Amount Due for 2016 Per Capita Fee"; myTable.Rows[14].Cells[4].Content = "$"; // Create variable for holding table tags text = myTable.MakeTags(); // Push variable into Array tables.push(text); } // Get Array Length ArraySize = tables.length; } // Cycle through Array until length is reached and print lines for (t = 0; t < ArraySize; t++) { tbl += tables[t]; } For what it's worth, I think you could simplify things a little bit by getting rid of the OnRecordStart rule all together and just do it all within your text rule: //The following if statement will detect if we are currently in preview mode or editing this rule (versus composing output). if(IsPreview() || FusionPro.inValidation) Rule("OnJobStart"); // Create empty var tables = []; var tbl = []; //Get a count of the total number of records in the external data file NumberOfRecords = data.recordCount; var type = [ ['Horses', 'Horses, Mules and Asses (ponies, donkeys, burros)', 5.85], ['Cattle','Cattle (cows, bulls, yearling)', 2.29], ['Bison','Domestic Bison', 6.38], ['Sheep','Sheep', 0.54], ['Swine','Swine', 0.78], ['Goats','Goats', 0.54], ['Poultry','Poultry (chickens, turkeys, gees, ducks and other domestic birds raised as food or to produce feathers', 0.05], ['Bees','Bees (number of hives or boards)', 0.41], ['Domestic','Alternative Livestock (privately owned caribou, mule deer, whitetail deer, elk, moose, antelope, mountain sheep, mountain goats indigenous to Montana)', 26.23], ['Ratites','Ratites (includes all ostriches, rheas and emus', 9.37], ['Llamas','Llamas and Alpacas' , 9.37] ]; //Now, loop through all records in the external data file and find the records that belong to the customer. for (var n = 1; n <= NumberOfRecords; n++) { function ExField(field) { return data.GetFieldValue(n, field); } if (ExField("CID") == Field("Customer ID")) { //Create Table var myTable = new FPTable; myTable.AddColumns(5825, 5400, 27200, 7200, 8100); tbl.push(["County", ExField("County"), "Property ID", ExField("PropertyID"), ""]); // Title tbl.push(["Head Count", "", "Livestock Type", "Fee Amount Per Head", "Per Capita Fee Calculated Totals"]); // Header tbl.push(["Previous Year", 'Current Year', '', '', '']); // Header type.forEach(function(s) { var [field, description, price] = s; field = ExField(field); price = FormatNumber('$00.00', price).replace('$0','$ '); tbl.push([field, '', description, price,'']); }); // Footer tbl.push(["Total Amount Due for 2016 Per Capita Fee", "", "", "", "$"]); // Formatting for (var i=0; i<tbl.length; i++) { var row = myTable.AddRow(); var cell = row.Cells[0]; cell.PointSize = (i == 2) ? 8 : 10; if (!i) row.Cells[3].HStraddle = 2; if (i == 1) { cell.HStraddle = 2; for (var c = 2; c<=4; c++) row.Cells[c].VStraddle = 2; } if (i == 14) cell.HStraddle = 4; var [col1,col2,col3,col4,col5] = tbl[i]; row.SetContents(col1,col2,col3,col4,col5); } // Push variable into Array tables.push(myTable.MakeTags()); } } return tables.join('');
  8. Can you post the rule in question or upload your template in a way that's compatible with FusionPro 8? Unfortunately, I am unable to view your file since I'm using an older version of FusionPro than you are. That being said, it sounds to me as if you're defining your array in OnJobStart (or JavaScript Globals) – essentially creating it once and pushing data into it per record. Does the third record's table have the contents of the first, second, and third record in it? If so, the solution could be as simple as resetting your array at the beginning of each record: var yourArray = []; // set yourArray to an empty array // code yourArray.push('your content'); Or more specifically to your scenario: var data = new ExternalDataFileEx('./property.txt','\t'); var yourArray = []; for (var i=1; i<=data.recordCount; i++) if (Field("Customer ID") == data.GetFieldValue(i, 'CID')) yourArray.push(i);
  9. Oh, sorry about that. I've fixed it in my original post. It should have said 'replace' – not 'join.' Dunno where that came from. In case you wanted to use 'join' instead of a 'replace' (which in my opinion is cleaner anyway) you could do so by using the following code: var pathName = "..\\RESOURCES\\"; var FullResourcePath = pathName + Field("Store Message"); //change to match your data file field var x = CreateResource(FullResourcePath,'graphic', 1); if (!x.exists) ReportError("Graphic not found: " + FullResourcePath); var result = []; for (var i=1; i<=x.countPages; i++){ x.pagenumber = i; result.push(x.content); } Print("Result is: " + result); return result.join('<p>\n');
  10. Is the 5th page blank? It could be because a paragraph tag is added to every page in the for loop. You could alter the code to remove the last paragraph tag by adding the line in red: var pathName = "..\\RESOURCES\\"; var FullResourcePath = pathName + Field("Store Message"); //change to match your data file field var x = new FusionProResource(FullResourcePath, "graphic", 1); if (!x.exists) ReportError("Graphic not found: " + FullResourcePath); var pdfString = ''; for (var pgnbr = 1; pgnbr <= x.countPages; pgnbr++) { x.pagenumber = pgnbr; pdfString += x.value + '<p>\n'; } Print("Result is: " + pdfString); return pdfString[color="Red"].replace(/<p>\\n$/,'')[/color];
  11. Did you see this thread? More specifically: this post. If that solution doesn't work for you, I suppose you could try: var cf = FusionPro.Composition.CurrentFlow; var fit = cf.fits && !cf.expandToFill; if (!fit) fit = +!RawTextFromTagged(cf.content.replace(/<variable name="([^"]*)">/g, function(s,p){return RuleOrField(p);})); if (!fit && !Copyfit(new MagnifyAttributes("text", 25, 400, 6, 72))) ReportWarning("Could not copyfit text in flow " + FusionPro.Composition.CurrentFlow.name);
  12. There are several ways that you can do this. Gregg has touched on how to accomplish this with static pages. You could even take that method a step further and only have two pages (a letter and a form) and repeat the record by the number of forms needed + 1 (for the letter) and then turn them on/off accordingly. I don't think editing the data is necessary in your case, though, unless you just want to save yourself a line or two of code. You can tell how many forms you'll need by pulling your "form" data in as an external data file and counting the number of records that match your internal data record's key. I'm assuming that the "key" linking the two data files in your case is the "ID" field (i.e. if record 1 has an "ID" field value of "1", the template should create a form for every record with a "1" in the "ID" field of the external data file). Here's an example of how to determine the number of forms for a given record on-the-fly: var data = new ExternalDataFileEx('./Test.Forms.csv', ','); var forms = 0; // number of forms for (var i=1; i<=data.recordCount; i++) if (data.GetFieldValue(i, 'ID') == Field("ID")) forms++; Another way you could accomplish this is to use a Template page (or RepeatableComponent) and an overflow page. Here's how: FusionPro > Manage Pages > Page Usage... Select your second page and change the "type" to "Overflow" and name it "overflow" FusionPro > Manage Pages > Insert Page Type: "Template", Name: "Form", Duplicate Existing Page 2 Draw a small text frame on the letter page (this will be used to overflow your forms) With the frame selected, click "Overflow..." in the frame properties window Select "Overflow text to new pages", New left page: "Overflow", Add pages: As few pages as possible Delete the FP frames from your overflow page (not the template page) and draw a large text frame that covers the page – it needs to be larger than the page itself. With the frame selected, click "Overflow to" in the properties window On the template page, you need to insert "fields" that you can populate from a rule. You can name them whatever you'd like but for the sake of example: open the text editor for the textbox beside "TIN:" and erase it's contents. In the "variable" field (don't select from the drop down list) type: "TIN" and press "insert". Your repeatable component now has a text variable named "TIN". Create a rule called "Form" containing the code below: //=================================================================================== // Function to create a 'Form' Repeatable Component // Requires 1 argument 'obj' which should be an Object // For example: // - obj = {'key' : 'value'}; // - 'key' is the name of the variable on the Template Page // - 'value' is the value to display for that variable //=================================================================================== function Form(obj) { if (!arguments.length) // If not arguments are passed, return nothing return ''; if (typeof obj != 'object') // If an object is not passed, throw an error ReportWarning('Expected an "object" but was passed a "' + typeof obj + '"'); // Create Repeatable Component constructor (name should match template page) var form = new FPRepeatableComponent('Form'); // Step through object values and assign them to their corresponding repeatable component variable // Replace leading/trailing spaces and commas to handle empty fields formatting for (var key in obj) form.AddTextVar(key, Str(obj[key]).replace(/^[,\s]*|[,\s]*$/g,'')); // Only return a form if at least one field is populated for (i in form.textVars) if (Str(form.textVars[i])) return form; // Otherwise, return an empty string. return ''; } var result = []; // Array of forms var data = new ExternalDataFileEx('./Test.Forms.csv', ','); // External Data file // Throw error if we can't find the external Data if (!data.valid) ReportError('Unable to locate external data file'); // Step through external data file and create forms for records that // match the 'ID' field of the current record. for (var i=1; i<=data.recordCount; i++) { // Just a function to make returning the value of the external data field a little cleaner function ExField(field) { return data.GetFieldValue(i, field) || ''; } // If the fields match, create the form if (ExField('ID') == Field("ID")) { // Object that maps Repeatable Component variables to their desired value var fields = { 'TIN' : ExField('Provider TIN'), /* // Map the rest of the fields here 'NAME' : NAME' : ExField('Practitioner First Name') + ' ' + ExField('Practitioner Last Name') + ', ' + ExField('Practitioner Degree'), 'NPI' : ExField('Practitioner NPI'), */ }; // Create the form by calling the 'Form' function and add it to the array result.push(Form(fields)); } } // Return the array of forms (repeatable components) return result.join('<p>'); Insert that rule into the text box you created on the "letter" page. I've attached an example template and proof of concept if you're interested. Please note that the attached is merely an example intended to demo my approach and since I am using an older version of FP than you are, the fields may differ (because I was just guessing when filling them in). ste_Multipg.zip proof.pdf
  13. Hm. Okay, that's interesting. Well maybe I still don't understand what you're asking but you can set individual leading on a per-text-box basis. In fact, you can specify differing leadings within the same text box. You just don't do that in the "Global" settings. As the name suggests, that globally affects all of your text flows that are using "auto-leading." If you want to specify a unique leading for the first paragraph, highlight the first paragraph in the text editor, click "Paragraph" and plug in a value for "absolute leading." If your paragraphs are variable or you just don't feel like fine tuning the leading for each paragraph, you could get Copyfit to do it for you. With your text box selected, click "Overflow..." on the text box's property window. Select "Adjust text to fit" and "allow text to expand to fill." Doing so will create an "OnCopyfit" call back rule in your list of rules. Replacing the contents of that rule with the code below will only adjust the leading in order to make text fit. if (!Copyfit(new MagnifyAttributes("leading", 25, 400, 6, 172))) ReportWarning("Could not copyfit text in flow " + FusionPro.Composition.CurrentFlow.name); You can adjust the values to your liking. In the above example, "25" is the minimum amount the leading is allowed to be magnified, "400" is the maximum. "6" is the minimum leading size (in points) and "172" is the maximum. Meaning: while FP is trying to make the text in a given frame fit, it will not shrink the leading below 6 point or 25% of its current size and it won't increase the leading to more than 172 points or 400% of its current size (which ever comes first). I also just want to mention (even though it's basically the opposite of what you asked) that you can have two linked text boxes with the above Copyfit applied to them that will optionally flow some of the first column to the second column and increase the leading in order to keep the formatting uniform across the two columns. I've attached a proof of both methods as well as the template that created them. example.zip proof.pdf
  14. When you say "line spacing," are you referring to leading? The first thing that comes to mind would be to make sure you aren't using "legacy line leading." From the text editor, click Paragraph > Global Settings > Use Legacy Line Leading. If that doesn't help or I've misunderstood the description of your problem, try and clear things up a bit and give us the version of FP/Acrobat/OS/etc you're running. It might also be helpful to post a screenshot of the behavior you're seeing compared to how you intend it to look.
  15. I believe you are correct in that assumption. I was just pointing out the danger of returned the 'firstName' variable after performing a replace on it if that had been your intention. And as I'm reading that, I realized that my solution doesn't correctly handle the "FIRST NAME" being empty. To account for that you'd have to add: var name = Field("FirstName"); if (/^\w\.?$/i.test(name) [color="Red"]|| !name[/color]) name = [ToTitleCase(Field("PREFIX").replace(/(\w)\.?\s*$/, '$1.')), Field("LAST NAME")].filter(String).join(' '); return name ? 'Dear ' + name + ',': ''; As rsheldon pointed out, your solution worked for him so I don't think my solution is "better" by any means. I just wanted to share an alternate method. As far as RegExp goes, that's another part that could be approached from many different angles but in particular to the two expressions I wrote, here is the break down using the RegExp Constructor to allow for easy commenting: exp = new RegExp( '^' + // Match starting at the beginning of the line '\\w' + // Matches an alphanumeric character '\\.' + // Matches a period '?' + // Matches the preceeding (in this case the period) 0 or 1 times '$', // End of the string 'i'); // Case-insensitive flag return exp.test('a.'); // returns true exp = new RegExp( '(' + // Starts a capture group - Saved as $1 '\\w' + // Matches an alphanumeric character ')' + // Closes the capture group '\\.?' +// Matches a period 0 or 1 times '\\s' + // Matches a space '*' + // Matches the preceeding character (in this case a space) 0 or more times '$', // End of the string ''); // No flags necessary here. return 'Mr '.replace(exp,'$1.'); // returns "Mr."
  16. Removing the periods in both cases is a good idea provided you're planning on adding them back. In the case of the "firstName" variable, a value of "A.J." gets altered to "AJ" but the periods are not added back. Also, I believe the if statement should be (note that you don't really need the 'else' statement since you're just going to return the line if the 'if' condition is met): if (firstName.length <= 1) return "Dear " + ToTitleCase(thePrefix) + "." + " " + Field("LAST NAME") + ","; [color="Red"]// else return "Dear " + Field("FIRST NAME") + ",";[/color] Another way to do this is using the test method. Which allows you to test whether a variable meets certain conditions or not (without altering the variable). For example: var name = Field("FirstName"); if (/^\w\.?$/i.test(name)) name = [ToTitleCase(Field("PREFIX").replace(/(\w)\.?\s*$/, '$1.')), Field("LAST NAME")].filter(String).join(' '); return name ? 'Dear ' + name + ',': ''; The code above sets the 'name' variable to the first name. Then it checks to see if 'name' only contains one letter (and potentially a period) regardless of the casing. If it is determined that the first name is an initial, the 'name' variable is set to the prefix followed by the last name. I should mention that I'm using an array to filter and join those values with a space in case one of them is empty. I'm also replacing the last letter of the "LAST NAME" field with the last letter and a period rather than adding a period to the prefix in case the prefix field is empty. Finally, if the 'name' field is not empty, it is prepended with 'Dear' and returned. If not, a null string is returned. The above code prevents lines such as: "Dear . Lastname," "Dear . ,"
  17. Sure, you can do that by inserting a "break" statement into your for loop (note that the brackets in orange aren't doing anything and aren't needed): returnStr = ''; if(FusionPro.Composition.isPreview == true || FusionPro.inValidation == true) { Rule("OnJobStart"); } numRecsExtDF = externalDF.recordCount; for (recordWalker=1; recordWalker <= numRecsExtDF; recordWalker++) { if (externalDF.GetFieldValue(recordWalker, 'USERACCOUNTNUMBER') == Field("USERACCOUNTNUMBER")) { [color="DarkOrange"]{[/color] returnStr += externalDF.GetFieldValue(recordWalker, 'USERID'); [color="Red"]break;[/color] [color="darkorange"]}[/color] } } return returnStr; Or, you could just return the line as soon as it's found (which is essentially the same thing): returnStr = ''; if(FusionPro.Composition.isPreview == true || FusionPro.inValidation == true) { Rule("OnJobStart"); } numRecsExtDF = externalDF.recordCount; for (recordWalker=1; recordWalker <= numRecsExtDF; recordWalker++) { if (externalDF.GetFieldValue(recordWalker, 'USERACCOUNTNUMBER') == Field("USERACCOUNTNUMBER")) { [color="darkorange"]{[/color] [color="red"]return externalDF.GetFieldValue(recordWalker, 'USERID');[/color] [color="darkorange"]}[/color] } } return returnStr; But the whole thing could be simplified by using the "FindRecord" method of the "ExternalDataFileEx" object. Like this: if(IsPreview() || FusionPro.inValidation) Rule("OnJobStart"); return externalDF.GetFieldValue(externalDF.FindRecord('USERACCOUNTNUMBER', Field('USERACCOUNTNUMBER')), 'USERID') || '';
  18. This is exactly what my 'Encode' function is doing. I probably should have mentioned this before, but here's the actual breakdown of how the function works: An array ('map') is used to hold the values of the possible check digits and barcode characters – I pulled these from the website I referenced in my previous post. The 'check' variable is initialized at 0 and will be incremented to determine the check sum The value of the barcode is set to uppercase Each character in the barcode is referenced against the 'map' array to determine how much to increment the check sum ('check'). For example: if the 'barcode' input is "EA", the for loop uses the 'map' array to determine the position of 'E' (map.indexOf('E') == 14) and adds 14 to the check sum. Then it gets the value of 'A' (map.indexOf('A') == 10) and adds 10 to the check sum. So the check sum for "EA" is "24". Then you need to do a mod 34 of the check sum: check = check % 34; or check %= 34 In the case of the "EA" example, 24 % 34 is 24 Finally the mod34 is referenced against the 'map' array to determine with value to return for the "check character" and appended to the end of the barcode. // map[24] == 'O' return barcode + map[check]; // returns EAO
  19. Can it be converted to JavaScript from Excel? I don't think so. But if you know what the formula is, you can write it in JavaScript for FusionPro to use. I googled "3 of 9 barcode formula" and came across this website. By referencing that website, I was able to come up with this function to create a human-readable barcode with a check digit: function Encode(barcode) { var map = ['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','-','.',' ','$','/','+','%']; var check = 0; barcode = ToUpper(barcode); for (i in barcode) check += map.indexOf(barcode[i]); check %= 43; return barcode + map[check]; } You can place it in your JavaScript Globals and call it from any rule in your template like so: return Encode('20000000'); // returns 200000002 In regards to your original question, if you're creating a sequential batch of barcodes, you could assign a starting point in JavaScript Globals or OnJobStart like this: var start = 20000000 - 1; // Minus one so the first record will be 20000000 Then your text rule would look like this: var seq = FusionPro.Composition.inputRecordNumber + start; return 'EA' + Encode(seq) + 'IN'; // returns EA200000002IN
  20. If you're composing this job using FPServer, you can adjust the text frame width based on the largest line. However, you'd be able to mimic that functionality by using a 1 column, 2 row table. In the example below, you set the 'width' variable to the width of your logo. If neither line exceeds that width then the table will be the same width as the logo and the text will be centered inside it. If either line is longer, the table's width will be set to match that of the longest line and the text will be centered inside. Since the table itself is left aligned, a line of text that is centered within a table of the same width will also appear to be left-aligned while the shorter lines appear centered. var address = '223 Blue Green Yellow Red Pkwy'; var csz = 'Anytown, USA 12345'; var width = 7200; // Width of logo. 7200 = 1" function getTextWidth(input){ var tm = new FusionProTextMeasure; tm.font = 'Helvetica'; // Your font tm.pointSize = '12 pt'; // Your font size tm.CalculateTextExtent(input); return tm.textWidth; } address = [address, csz]; for (var i in address) if ((w = getTextWidth(address[i])) > width) width = w; var table = new FPTable(); table.AddColumn(width*1.001); for (var i in address) { var row = table.AddRow(); var cell = row.Cells[0]; cell.Margins = {Top:10, Bottom:10, Right:0, Left:0}; cell.HAlign = 'Center'; row.SetContents(address[i]); } return table.MakeTags(); Make sure to define your font and font size within the 'getTextWidth' function and check "Treat returned strings as tagged text."
  21. If I understand correctly, you want each page of each record to be output to its own PDF and named accordingly? So you'd end up with 17 PDFs for record one named: "Record1_Page1.pdf" "Record1_Page2.pdf" etc... But, for proofing purposes you'd like all of the pages to be output to one PDF named: "Record1_proof.pdf" "Record2_proof.pdf" You can do that by putting this into your OnRecordStart rule: var proof = true; var page = 'proof'; var pageNames = [ 'Page1', 'Page2', 'Page3', // .. etc .. 'Page17' ]; if (proof) for (var i in pageNames) FusionPro.Composition.SetBodyPageUsage(pageNames[i], true); else { FusionPro.Composition.repeatRecordCount = pageNames.length; page = pageNames[FusionPro.Composition.repeatRecordNumber-1]; FusionPro.Composition.SetBodyPageUsage(page, true); } FusionPro.Composition.OpenNewOutputFile('Record' + FusionPro.Composition.inputRecordNumber + '_' + page + '.' + FusionPro.Composition.outputFormatExtension); Assuming you don't want to create a proof each time you compose the template, you can adjust the output behavior by changing the 'proof' variable. As the name suggests, if 'proof' is set to true, a 17 page PDF is output per record; if it's set to false, 17 single-paged PDFs are output per record. If for some reason you want to output single pdfs and proofs every time you run the job, you can use this code instead: var pageNames = [ 'Page1', 'Page2', 'Page3', // .. etc .. 'Page17' ]; FusionPro.Composition.repeatRecordCount = pageNames.length + 1; page = pageNames[FusionPro.Composition.repeatRecordNumber-1] || 'proof'; if (page == 'proof') for (var i in pageNames) FusionPro.Composition.SetBodyPageUsage(pageNames[i], true); else FusionPro.Composition.SetBodyPageUsage(page, true); FusionPro.Composition.OpenNewOutputFile('Record' + FusionPro.Composition.inputRecordNumber + '_' + page + '.' + FusionPro.Composition.outputFormatExtension);
  22. On your server is the J drive mapped to the same location as it is on your local machine? I imagine that it's not. In fact, I bet you have several lines in your .msg log informing you that the server is unable to find a graphic at that location. I would suggest using UNC paths for those graphics. Find out what the J: drive is mapped to on your local machine: 1. Open Command Prompt (Start > Run.. > cmd > enter) 2. Enter the following and make a note of the "remote" that corresponds with "J:": net use Then change your code accordingly: path = '\\\\remote\\server\\'; Pic = CreateResource(path + "BundleSlipSheet-Output.pdf", "graphic", true); path = '\\\\remote\\server\\'; Pic = CreateResource(path + "Production\\Job 46759 OH8201011-Output"+Field("ID")+".pdf", "graphic", true);
  23. You can make a rule to return them all with the appropriate formatting: fields = []; i = 0; while (i < 18) { r=[]; while (r.length<2 && i<18) if (a = Field('improve_' + ++i)) r.push(a); fields.push(r.join('<t>\t')); } return fields.join('<br>\n') Make sure that that "Treat returned strings as tagged text" is checked.
  24. Okay cool. Good to know. How are you adding the "sections?" I'm assuming you have 3 separate text rules set up to handle each section and each of those 3 pages is allowed to overflow if needed with the section-specific filler page following the overflow page in case it's needed. Is that correct? If you wanted to simplify things and do it all within the onRecordStart rule, with only one text frame and one overflow page you'd be able to if you brought in your "filler" pages as resources instead of making them part of your template – just something to think about. I will come back to this topic momentarily. 'totalPages' is not a property of FusionPro resources so 'Pic.totalPages' won't work – you'd have to write 'Pic.countPages.' Aside from that, you're stepping through each field in that array and turning on/off the filler page based on whether the number of pages in each resource is odd/even. What you need to do is pull the part where you're toggling the filler page out of that for loop and only do it once based on the total page count (PAGECount): PAGEArray = [Field("FundCare_Orange1"), Field("Dental_Orange2"), Field("FlexSpend_Orange3"), Field("PlansWork_Orange4"), Field("HowToHSA_Orange5"), Field("ClientUpload_Orange")] var PAGECount = 0; for (i = 0 ; i < PAGEArray.length ; i++) { if (PAGEArray[i] != "") { var Pic = new FusionProResource(PAGEArray[i], "graphic", "true"); var PicPages = Pic.countPages; PAGECount += PicPages; } } [color="Red"]if (PicPages % 2 > 0) { FusionPro.Composition.SetBodyPageUsage("Orange_Filler1", true); } [/color] Print("PAGECount is " + PAGECount) After your clarifications and if my aforementioned assumptions are correct, I think what you want to do is this: var pages = 14; // Number of static pages var endPages = ['End Filler Left','End Filler Right']; // Names of the Filler Pages at the end // Sections (Named in accordance to their "filler" page) sections = { 'Orange_Filler1': [ Field("FundCare_Orange1"), Field("Dental_Orange2"), Field("FlexSpend_Orange3"), Field("PlansWork_Orange4"), Field("HowToHSA_Orange5"), Field("ClientUpload_Orange") ], 'Green_Filler1': [ Field("Green1") ], 'Blue_Filler1': [ Field("Blue1"), Field("Blue2"), Field("Blue3"), Field("Blue4") ] }; for (i in sections) { pgCount = 0; sections[i].filter(String).forEach(function(s){ pgCount += CreateResource(s, 'graphic').countPages; }); pages += pgCount; if (pgCount % 2) FusionPro.Composition.SetBodyPageUsage(i, pages++); } var pagesNeeded = 4-(pages%4 || 4); while (pagesNeeded--) FusionPro.Composition.SetBodyPageUsage(endPages[pagesNeeded], true); Here's what it's doing: The 'sections' object has 3 properties: 'Orange_Filler1', 'Green_Filler1' and 'Blue_Filler1'. These are the names of your "filler" pages. I do realize that you probably don't have a "green" filler page because it'll be even either way (2 pages or 0 pages) but the other names need to match what you've named your body pages so make adjustments as needed. The value of each property is an array of the fields that hold the name of the resource used to comprise each section. The 'for' loop steps through the 'sections' object and: Filters each array (removing empty fields) Creates a resource from the remaining fields in order to get a page count Updates the total number of pages (pages) And depending on whether or not the section is odd/even, the filler sheet is activated and the page count is incremented to account for the filler sheet Finally, a while loop is used to turn on extra pages to make the total page count divisible by 4. As I mentioned before, you can also use this code to assign the sections to a single frame that is set to overflow (assuming that you create resources for your "filler" pages named by the 'sections' property to which they correspond ('Orange_Filler1.pdf'): var frameName = 'TextFrame'; // Name of Text Frame to display sections var result = []; // Array to hold sections var pages = 14; // Number of static pages var endPages = ['End Filler Left','End Filler Right']; // Names of the Filler Pages at the end // Sections (Named in accordance to their "filler" page) sections = { 'Orange_Filler1.pdf': [ Field("FundCare_Orange1"), Field("Dental_Orange2"), Field("FlexSpend_Orange3"), Field("PlansWork_Orange4"), Field("HowToHSA_Orange5"), Field("ClientUpload_Orange") ], 'Green_Filler1.pdf': [ Field("Green1") ], 'Blue_Filler1.pdf': [ Field("Blue1"), Field("Blue2"), Field("Blue3"), Field("Blue4") ] }; function addPage(res) { res = CreateResource(res, 'graphic'); for (var n=1; n<=res.countPages; n++) { res.pagenumber = n; result.push(res.content) pgCount++ } } for (i in sections) { pgCount = 0; sections[i].filter(String).forEach(function(s){ addPage(s); }); if (pgCount % 2) addPage(i); pages += pgCount; } FindTextFrame(frameName).content = result.join('<p>'); var pagesNeeded = 4-(pages%4 || 4); while (pagesNeeded--) FusionPro.Composition.SetBodyPageUsage(endPages[pagesNeeded], true);
  25. Okay. How many pages is the book if the client does not select any additional pages in your two sections? I'm going to assume that it's a number divisible by 4. That's a good start. I'm going to assume that each of those fields in the PAGEArray represent 1 of 5 potential pages. If that's the case, then that would explain why your "Orange_Filler1" page is being turned on when the page count is odd (or 1 in this case). Moreover, if each PDF resource referenced in those fields is 1 page, then there's really no point in counting the pages or even converting it to a FusionPro Resource for that matter. Instead you could filter the array and determine the page count by the length of the array like this: var docs = [Field("FundCare_Orange1"),Field("Dental_Orange2"),Field("FlexSpend_Orange3"),Field("PlansWork_Orange4"),Field("HowToHSA_Orange5"),Field("ClientUpload_Orange")].filter(String); FusionPro.Composition.SetBodyPageUsage("Orange_Filler1", docs.length % 2); Well that's because you defined PAGECount as 0 and never incremented it. In order to make your Print statement work, you'd have to make the following revisions: PAGEArray = [Field("FundCare_Orange1"), Field("Dental_Orange2"), Field("FlexSpend_Orange3"), Field("PlansWork_Orange4"), Field("HowToHSA_Orange5"), Field("ClientUpload_Orange")] var PAGECount = 0; for (i = 0 ; i < PAGEArray.length ; i++) { if (PAGEArray[i] != "") { var Pic = new FusionProResource(PAGEArray[i], "graphic", "true"); var PicPages = Pic.countPages; [color="Red"]PAGECount += PicPages;[/color] if (PicPages % 2 > 0) { FusionPro.Composition.SetBodyPageUsage("Orange_Filler1", true); } } } Print("PAGECount is " + PAGECount) Are you saying you don't know how many pages are in your template? Maybe you could try this: var endPages = [ // These are the names of the pages that will be // turned on to make the totalPage count == 4 'FillerPage1', 'FillerPage2', 'FillerPage3' ]; var pagesNeeded = 4-(FusionPro.Composition.totalPages%4 || 4); for (var i = 0; i < pagesNeeded; i++) FusionPro.Composition.SetBodyPageUsage(endPages[i], true); And if you feel like collecting your template or posting a sample, that would probably clear up a lot of the things I just guessed on. Good luck!
×
×
  • Create New...