We have a VB.NET program that is using Supergoo's ABCPDF version 6.1.1.2. Our program takes standard XML strings and places values in a corresponding PDF form field on the template PDF.
Problem:
We have over 3000 PDF files that have all been "tagged" with form fields. There could be up to 50 form fields on the template PDFs for a total of roughly 150,000 form fields in use potentially. We have noticed that some of these form fields have their form field common properties set to hidden by mistake. (see screenshot)
The issue is that the PDF coming back after the string values have been added are not showing up. Duh right? Fix the form field property and call it done. However, there is no way to know how many other of the other 150,000 form fields have been improperly tagged like this.
Does anyone know if I can adjust the PDF generation program to forcefully ignore that form field common property? Here is a sample of the vb.net code I am hoping to slightly alter...
Dim theDoc As Doc = New Doc
theDoc.Form.Fields("SampleFieldName").?????? 'can we set something here to ignore the hidden property?
According to the docs at
http://www.websupergoo.com/helppdfnet/source/6-abcpdf.objects/field/2-properties/page.htm
The .Page property of a Field object will tell you the page the field is on. Since Page is a class, if the result 'Is Nothing' then you know that the field is not visible since it doesn't appears on any page in the PDF document.
Please note that there are a few caveats when using fields that are not hidden but not actually visible when rendered (being too small, being spread on two pages, etc). If you need need to handle that you may be interested in http://www.websupergoo.com/helppdfnet/source/6-abcpdf.objects/field/2-properties/rect.htm depending on your use cases.
For the ABCPDF v6 software, I have discovered through Mihai's suggestion that it is possible. I have coded this C# example in the hopes that it helps someone down the road...
static void SetFillableFieldsToWriteableExample(string origFileLocation, string newFileLocation)
{
Doc theDoc = new Doc();
theDoc.Read(origFileLocation);
var theFields = theDoc.Form.GetFieldNames();
foreach (string theField in theFields)
{
Field theFieldInstance = theDoc.Form[theField];
theDoc.SetInfo(theFieldInstance.ID, "/F", "4");
}
theDoc.Save(newFileLocation);
}
I have tested this and it works when all fields are text fields on the PDF. Not sure on the other field types.
This code should not be used in a production environment as written here. There is no guarantee that the origFileLocation or newFileLocation references a PDF and no error handling among other issues. This is for demonstration purposes only.
Related
I'm trying to automate populating a PDF form from MS Access VBA. The form itself is maintained online and so my process is:
Download the form
Open it in Adobe
Auto-populate fields
Save / send it on to others
All of this I'm doing from Access VBA. I've encountered an issue on step 3, however.
What I noticed was, if I try to populate the field from VBA, it works fine if the field already has a value. However, on a new / blank form, it generates an error.
Specifically, I call it using the document Javascript object (jso) thusly:
jso.xfa.resolveNode("form." & jso.getNthFieldName(0)).rawValue = 'test'
If the form field is not populated, VBA errors, saying the object doesn't have the rawValue property.
I'm guessing some sort of model initialization is forestalled until the form is populated at least once. The field reference exists in the jso.xfa model, but the property itself is otherwise inaccessible.
Is there a way around this other than pre-populating a "dummy" form? I'd rather not have to maintain / update it as the original gets changed online.
I have an unbound textbox to accept the delete older than: number of days. It is in the report header. I set it to 30 days but I want the user to be able to change it. I was banging my head trying to figure out why entering 40 was not being accepted and it reverted back to 30 every time. I finally decided on using the lost_focus event to set .value to .text. That worked.
Further research showed that when the textbox get's focus text and value are both the same, 30 in my case. Changing the number in the text box to 40 shows the values of text at 40 and value at 30. Unless I specifically set Value to the value of text Access changes text to the value of value. This is different behavior than other places in Access such as forms.
Can anyone tell me why this might be? I can't find any setting that might do this. Is it because it's in a report header? what is the difference between this and every other text box I've ever used?
From a "best practices" viewpoint, Access Reports are not intended to be used interactively despite the ability to manipulate some unbound controls. Although workarounds can be implemented that function sufficiently well, such solutions are often incomplete and buggy and function differently depending on the active view: Report View vs. Print Preview. Appropriate design patterns include using Access Forms for specifying report options which then open the Report in a static configuration.
This may not satisfy the question "Why?" if seeking a deeper answer as to why Microsoft implemented inconsistent binding behavior in Access, or why they allowed interactive controls in reports at all if they don't behave the same way as in forms. But Access has plenty of other quirky behaviors that have no known/published explanation.
Regarding the priority of the Value property updating the Text property (and not vice versa): Value is the key field because it contains the actual data for the control (bound or unbound). Although it is natural to have a single control for both display and input (uh, that's how almost all controls work), the processes of displaying data and parsing user input are two distinct functions. The visual representation returned by the Text property can be manipulated using the various formatting properties, and technically could display an incomplete representation of the underlying Value data. If there are any conflicts between the stored Value property and the Text property, it is natural that the existing Value property has precedent.
My guess is that the automatic binding behavior was "relaxed" for reports to allow more flexible custom reporting output. First consider an Access Form in Datasheet view: An unbound Form control shows the same value for all records. Even if the control is edited while on a particular row, the updated value is displayed for all rows. The same control object is essentially repainted for each row and there is no concept of individual instances of the control that can hold different values. Bound controls have built-in code that repaint the control with data from the particular row, but there are still not multiple instances each "holding" the individual values. The visual output differs from an intuitive object-oriented paradigm where our minds what to assign each visual row its own in-memory instance of the controls--it just doesn't work like that in Access.
Unlike the Form behavior just described, the Report's Print Preview (and actual printed output) allows unbound controls to display different data per row using the Detail_Format() event. Within the Detail_Format() event, one can set the Value property of a control at which time the Text property is automatically updated according to various formatting properties. This update Text is then output for the current row. Perhaps (just guessing) that this behavior would not function properly if the Text property updated the value property. I suspect it would cause recursive events during report generation. Because reports are not meant to be interactive, relevant text-input parsing code was "disconnected" so that it doesn't behave like on a form.
All that explanation doesn't make Access any less frustrating nor remove its limitations, but at least learn to adapt and design things in the "Access-esque" way rather than fighting it.
your best bet is to design a form with the unbound combo boxes and have your data displayed in a subreport. I like to design my reports so that when values are updated the query for the recordsource of the report is generated doing this requires 2 queries to exist, one with all data possible and a filtered one as subreport recordsource. This will control the data for printing and also allow users to close or navigate away from the report and return to the data later.
Private Sub ComboBox1_AfterUpdate()
Dim Query1 as Object
Dim Temp_Name as Variant
Temp_Name = SubReport.SourceObject
SubReport.SourceObject = Empty
Set Query1 = Me.Form.Application.DBEngine.Workspaces(0).Databases(0).QueryDefs ("SubReport_Query")
Query1.SQL = "Select * Unfiltered_Query WHERE Field1 <= " ComboBox1 & ";"
SubReport.SourceObject = Temp_Name
End Sub
I would like to know if it's possible to add additional attributes to simple Range text or Paragraph text from my Word Add-in. For simplicity see code below where I can populate the document with text but additionally i would like to store additional but behind the scenes info along with that text. Ultimately I want these read these custom attributes using the Open Xml SDK when these documents go through a processing stage.
private void AddAttributedContent(string documentContent)
{
var doc = Globals.Factory.GetVstoObject(Globals.ThisAddIn.Application.ActiveDocument);
var range = doc.Range();
range.Font.Size = 12;
range.Font.Name = "Calibri";
range.Underline = Word.WdUnderline.wdUnderlineSingle;
range.Text = documentContent;
// range.AddOpenXmlProperty("MyProp", "MyValue");
var para = doc.Paragraphs.Add();
para.Range.Text = documentContent;
//para.AddCustomProperty("MyProp", "MyVal");
}
Edit:
Ideally our property would sit inside of the resulting RunProprties :
No, you can't do that. You could probably do it with a content control or a text box though.
For a ContentControl, you could probably use Tag (if this is like sources, it probably has to be unique, though the description seems to imply it's meant for the purpose you need) or Title.
The Tag property is different from the Title property in that a tag is never displayed while a user edits a document. Instead, developers can use it to store a value for programmatic manipulation while the document is opened.
For a text box (a Shape), you could use AlternativeText or Title.
Note that altering Title (in both cases) or AlternativeText will probably affect the way the document is displayed if you save it as HTML.
I am trying to export items from my access database into lotus notes. The document I am trying to export to is a stationary, and has all the data written into it, I just need to somehow mark placeholders and then update the values. I have read the documentation and it appears I will need to address fields and then call a method to replace the text like so:
'where body is the field and the following string is what to replace field with
Call doc.ReplaceItemValue("body", "REPLACE BODY")
To be clear, my entire code looks like:
Set session = CreateObject("Notes.NotesSession")
Set maildb = session.GetDatabase("server", "mail\box.nsf")
Set View = maildb.GetView("Stationery")
Set entries = View.AllEntries
Set entry = entries.GetFirstEntry
Set doc = entry.Document
Call doc.ReplaceItemValue("Subject", "Report - " & Date)
'add code here
Call doc.send(False, "person.to.receive#thisemail.com")
End Sub
I have noticed that while perusing documentation, there seems to be an ability to create fields, and then address those fields to update values. So for example, if I have a field named $COST, then one could do:
Call doc.ReplaceItemValue("$COST", "The cost is $3000")
And that field should be updated to reflect the value I passed through the method. My big problem is, even looking through documentation, I cannot figure out where I need to go to add in my custom fields. It seems that the documentation assumes that you know how to create these fields and just address them. Or am I only supposed to create these fields programatically and then fill in the data? My client is Lotus Notes 8. Thanks!
Yes, that is the cool thing about IBM Lotus Notes databases: you can put items (=fields) in a Notes document without a prior definition of fields.
If you create items in a document with doc.ReplaceItemValue() and save or send the document then the items are just there. You can check the items when you open the property box for a selected document. All items are listed on document properties' second tab.
Another question is of course to define fields in a form so that the created items are visible to user without looking at document properties box. Open database in Designer and put the fields in right position and size to form.
Your question and comments telling that you want to create a document, fill it with data and send it to users.
If all users have access to your Notes server then you can create that document in your existing database and send just a link mail to users. This way you can create a good looking form and position all your data fields. Users will access the document in database through link.
An alternative is to create an nice looking HTML file, attach it to the mail and send it.
In this case you would add this code to your example at 'add code here:
Call doc.RemoveItem("Body")
Set rtitem = doc.CreateRichTextItem( "Body" )
Call rtitem.AppendText("your mail text")
Call rtitem.EmbedObject(EMBED_ATTACHMENT, "", "report.html")
Based on the comment thread on #Knut Herrmann's answer, I believe that the solution you really want involves using "stored form". The first argument to the NotesDocument.Send() method is a boolean that specifies whether you want to store the form or not.
Normally, you would use Domino Designer to create a stored form. You would not need Designer rights to anyone's mailbox. You would just need to create an empty database of your own, and put a form into it. You woould change your code to open that database and create the document in there instead of in a mailbox database as you are doing now. (One of the other cool things about Notes is that you don't actually have to be working in a mailbox database in order to mail a document. You can mail any document from any database, as long as you put the approporiate fields into it.)
There is also a way to do this without Domino Designer, and you could even dynamically generate the form with truly custom fields that your code only discovers as it runs. You could do this with DXL, which is an XML format for describing Lotus Notes objects, including forms. You would just need some sample DXL to work from. Preferably that should be of an empty database that contains a simple form that is set up more or less in the layout that you would want, though again you would need Domino Designer for that. You could just use the same mailbox database that your code is currently using, but that will leave you with a lot of extra stuff in the DXL that doesn't need to be there; and given that you're not all that familiar with Notes, it would likely be difficult for you to navigate through it all to find what you need.
Either way, though, you could use the NotesDXLExporter class to generate the DXL file. Your code could manipulate the DXL, adding/changing elements as needed (following the pattern that you see in sample, of course), and they you could use NotesDXLImporter to create the database that your code will actually use to create the document in and mail the message with the stored form.
In onw of our application, we have some reports that need very specific margins that are dependant of the printer the user have. It is used to print into preformatted paper with blanks to fill and depending of the printer, the printing is off by some margins. To make things worse, we don't actually control what printer is used because it is not an internal application.
The only solution I could think of is to let the user configure the margins somehow. I thought I could just dynamically change the report margins like I did with datasources and actual report (I have one control that is used to display every reports from my application and it works alright), but I can't seem to find that damn property to do so. There is a margin property on the report viewer but it's for the form display so it doesn't cut it.
Does anyone knows how to. What I was thinking to do is to define the margins before the user loads the report, i.e. when he clicks on the report button, I load the report, set the margins (or vice-versa is necessary) and then display it.
Before someone mention it, I know the user can, once the report is loaded, change the page setup to fit his needs, but this has two drawbacks. First one is that it is not saved each time and I need it to be 'saveable' and by users. The second one is that Report viewer seems to have some bugs when the regional setting aren't set to what it's expecting and we can't force the users to changes their setting to accommodate one application.
Edit: Forgot to mention, it this is of any uses. My reports are all local reports.
'creates a new page setting
Dim instance As New PageSettings()
'create the new margin values (left,right,top,bottom)
Dim value As New Margins(0, 0, 0, 0)
'gives your new pagesetting a value
instance.Margins = value
'report viewer now sets your margins
ReportViewer1.SetPageSettings(instance)
You can't do this directly. Try controlling your margins in the report with report parameters.
Well, I just had this similar problem; needed a report to have 0" margins, otherwise the blasted thing went from 2 pages to 6, and it split the data vertically, so the pages made no sense. I was having to set it manually in the Print Setup to print, but the end goal of this report is to be emailed to customers as a PDF, and guess what, when I set the margins manually and exported, it still split into 6 pages and bombed out. So risking everything, I tried one last supreme effort to fix this blasted thing - I opened up the .rdlc with word pad, and miracle of miracles, found 1in, etc. I set all those buggers to 0in, saved, ran my .net code (2008), opened up Print Setup, and hallelujah, there was my 0in settings. Now for the supreme test - I exported it as a PDF, went to the file, and son of a gun! Sweet. There were two crisp clean perfectly laid out pages. All was beneficent in the universe, I calmed down and let the midiclorians flow through me, and just basked in a moment so rare that it is rarely realized by some - a simple fix.
Check out the ReportPageSettings class, part of the report viewer control. Setting those values looks like it should get you what you need.
More settings;
Dim myPageSettings As New PageSettings()
myPageSettings.Margins = New Margins(0, 0, 0 , 0)
Dim paperSize As PaperSize = New PaperSize()
'ToDo: update with the PaperKind
'that your printer uses
paperSize.RawKind = PaperKind.A4
' paperSize.RawKind = System.Drawing.Printing.PaperKind.A4
myPageSettings.PaperSize = paperSize
'False for "Portrait"
'True for "Landscape"
myPageSettings.Landscape = False
'report viewer now sets your margins
ReportViewer1.SetPageSettings(myPageSettings)
this code work with me, the numbers in mm
Dim newPageSettings As New System.Drawing.Printing.PageSettings
newPageSettings.Margins = New System.Drawing.Printing.Margins(50, 100, 45, 45)
ReportViewer1.SetPageSettings(newPageSettings)
im using vb.net 2013