OpenXML insert into content control Missing Word.Text - c#

I have written the following code to insert some text in a contentcontrol in the footer of a document.
oItem.File.CheckOut();
byte[] byteArray = oItem.File.OpenBinary();
using (MemoryStream mem = new MemoryStream())
{
mem.Write(byteArray, 0, (int)byteArray.Length);
using (WordprocessingDocument wp = WordprocessingDocument.Open(mem, true))
{
Boolean foundInFooter = false;
MainDocumentPart mainPart = wp.MainDocumentPart;
foreach (FooterPart footerPart in mainPart.FooterParts)
{
Word.Footer footer = footerPart.Footer;
foreach (Word.SdtElement sdt in footer.Descendants<Word.SdtElement>().ToList())
{
Word.SdtAlias alias = sdt.Descendants<Word.SdtAlias>().FirstOrDefault();
if (alias.Val.Value == "Revisionsnummer")
{
foundInFooter = true;
if (sdt.Descendants<Word.Text>().FirstOrDefault() != null)
{
sdt.Descendants<Word.Text>().FirstOrDefault().Text = (string)oItem["Version"];
}
}
}
}
}
}
for some reason sometimes the sdt.Descendants<Word.Text>().FirstOrDefault() return null so i cant insert text. Is there anyway in theese cases to add the Word.Text ?

The premise of .First/Single/OrDefault is so that you can check the result of your expression prior to using it. e.g.
var obj = sdt.Descendants<Word.Text>().FirstOrDefault();
if(obj!=null)
{
obj.Text = (string)oItem["Version"];
}
else
{
...
}
If you automically try to assign a value to the result set of a OrDefault you will be setting yourself up for null reference exceptions.

Related

WordprocessingDocument doesnt save as expected in the memory stream

I am trying to make modifications in word document. For some reason the row that I added doesnt get saved in the memory. What I am doing wrong, no errors just the changes are not saved.
public void Generate()
{
byte[] templateDoc = File.ReadAllBytes(#"C:\Desktop\Test\11.docx");
using (MemoryStream stream = new MemoryStream())
{
stream.Write(templateDoc, 0, (int)templateDoc.Length);
using (var document1 = WordprocessingDocument.Open(stream, isEditable: true))
{
foreach (var item in document1.MainDocumentPart.Document.Body)
{
if (item.InnerText.Contains("<test>"))
{
DocumentFormat.OpenXml.Wordprocessing.Table table = new DocumentFormat.OpenXml.Wordprocessing.Table();
DocumentFormat.OpenXml.Wordprocessing.TableRow tr1 = new DocumentFormat.OpenXml.Wordprocessing.TableRow();
DocumentFormat.OpenXml.Wordprocessing.TableCell tc1 = new DocumentFormat.OpenXml.Wordprocessing.TableCell();
tc1.Append(new TableCellProperties(new TableCellWidth() { Type = TableWidthUnitValues.Pct, Width = "50" }));
tc1.Append(new DocumentFormat.OpenXml.Wordprocessing.Paragraph(new Run(new Text("Input 1:"))));
tr1.Append(tc1);
table.Append(tr1);
document1.MainDocumentPart.Document.Body.Append(table);
}
}
File.WriteAllBytes(#"C:\Desktop\Test\22.docx", stream.ToArray());
}
}
}

OpenXml how to split a word document into pages

I want to split a Word document into separate pages and save them to files like Page_1.docx; Page_2.docx ......
At first I tried to do this:
using (var sourceWordDoc = WordprocessingDocument.Open(
wordFilePath
, false))
{
var sourceElements = sourceWordDoc.MainDocumentPart.Document.Body.Elements();
var pageElements = new List<DocumentFormat.OpenXml.OpenXmlElement>();
var pageIndex = 1;
foreach (var sourceElement in sourceElements)
{
var run = sourceElement.GetFirstChild<Run>();
if (run != null)
{
var lastRenderedPageBreak = run.GetFirstChild<LastRenderedPageBreak>();
var pageBreak = run.GetFirstChild<Break>();
if (lastRenderedPageBreak != null || pageBreak != null)
{
//Create and save page
using (var destinationWordDoc = WordprocessingDocument.Create(
$"Page_{pageIndex}.docx",
DocumentFormat.OpenXml.WordprocessingDocumentType.Document))
{
var destinationPart = destinationWordDoc.AddMainDocumentPart();
destinationPart.Document = new Document();
var destinationBody = destinationPart.Document.AppendChild(new Body());
foreach (var pageElement in pageElements)
{
destinationBody.Append(pageElement.CloneNode(true));
}
destinationPart.Document.Save();
}
pageElements.Clear();
pageIndex++;
}
}
pageElements.Add(sourceElement);
}
}
But in this variand, Header is not copied.
Then I wrote the following code to copy the Header (for test):
using (var sourceWordDoc = WordprocessingDocument.Open(
wordFilePath
, false))
{
var sourceHeaderPart = sourceWordDoc.MainDocumentPart.HeaderParts.FirstOrDefault();
var sourceDocument = sourceWordDoc.MainDocumentPart.Document;
using (var destinationWordDoc = WordprocessingDocument.Create(
somePath
, WordprocessingDocumentType.Document))
{
var destinationMainDocumentPart = destinationWordDoc.AddMainDocumentPart();
destinationMainDocumentPart.Document = new DocumentFormat.OpenXml.Wordprocessing.Document();
var destinationBody = destinationMainDocumentPart.Document.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Body());
var rId = string.Empty;
if (sourceHeaderPart != null)
{
var destinationHeaderPart = destinationMainDocumentPart.AddNewPart<HeaderPart>();
rId = destinationMainDocumentPart.GetIdOfPart(destinationHeaderPart);
destinationHeaderPart.FeedData(sourceHeaderPart.GetStream());
}
var sourceElements = sourceDocument.Body.Elements();
foreach (var sourceElement in sourceElements)
{
if (sourceElement.GetType() == typeof(DocumentFormat.OpenXml.Wordprocessing.SectionProperties))
{
var sourceSectionsElements = sourceElement.Elements();
foreach (var sourceSectionsElement in sourceSectionsElements)
{
if (sourceSectionsElement.GetType() == typeof(DocumentFormat.OpenXml.Wordprocessing.HeaderReference))
{
var sourceHeaderReference = (DocumentFormat.OpenXml.Wordprocessing.HeaderReference)sourceSectionsElement;
sourceHeaderReference.Id = rId;
break;
}
}
}
destinationBody.Append(sourceElement.CloneNode(true));
}
}
}
But now I have a new problem, images from the header are not transferred.
Are there any options to break the document into separate pages and at the same time preserve the entire content of the page?

csvHelper Put values from different lines into one filed in Database

I have a .csv file structured like so, first row is the header column, for each SomeID I need to add the NetCharges together(or substract if the code calls for it) and put each item into its own column by the SomeCode column.
Heres the file I receive;
SomeID,OrderNumber,Code,NetCharge,Total
23473,30388,LI 126.0000, 132.00
96021, 000111, LI, 130.00, 126.00
23473,30388,FU 6.0000, 132.00
4571A,10452,LI,4100.0000, 4325.0000
4571A,10452,FU,150.00,4325.0000
4571A,10452,DT,75.00,4325.0000
I need to insert the data to my sql table which is structured like this. This is what I'm aiming for:
ID OrderNumber LICode LICodeValue FUCode FUCodeValue DTCode, DTCodeValue, total
23473 30388n LI 126.000 FU 6.0000 NULL NULL 132.0000
4571A 10452 LI 4100.0000 FU 150.0000 DT 75.00 4325.0000
My SomeID will not always be grouped together like the 4571A id is.I basically need to iterate over this file and create one record for each SomeID. I cannot seem to find a way with csvHelper. I'm using C# and csvHelper. I have trid this so far but I cannot get back to the SomeId after passing on to the nexr one:
using (var reader = new StreamReader( "C:\testFiles\some.csv" ))
using (var csv = new CsvReader( reader, CultureInfo.InvariantCulture ))
{
var badRecords = new List<string>();
var isRecordBad = false;
csv.Configuration.HasHeaderRecord = true;
csv.Configuration.HeaderValidated = null;
csv.Configuration.IgnoreBlankLines = true;
csv.Configuration.Delimiter = ",";
csv.Configuration.BadDataFound = context =>
{
isRecordBad = true;
badRecords.Add( context.RawRecord );
};
csv.Configuration.MissingFieldFound = ( s, i, context ) =>
{
isRecordBad = true;
badRecords.Add( context.RawRecord );
};
List<DataFile> dataFile = csv.GetRecords<DataFile>().ToList();
//initialize variable
string lastSomeId = "";
if (!isRecordBad)
{
foreach (var item in dataFile)
{
// check if its same record
if (lastSomeId != item.SomeID)
{
MyClass someClass = new MyClass();
lastSomeId = item.SomeID;
//decimal? LI = 0;//was going to use these as vars for calculations not sure I need them???
//decimal? DSC = 0;
//decimal? FU = 0;
someClass.Id = lastSomeId;
someClass.OrdNum = item.OrderNumber;
if (item.Code == "LI")
{
someClass.LICode = item.Code;
someClass.LICodeValue = item.NetCharge;
}
if (item.Code == "DT")
{
someClass.DTCode = item.Code;
someClass.DTCodeValue = item.NetCharge
}
if (item.Code == "FU")
{
someClass.FUCode = item.Code;
someClass.FUCodeValue = item.NetCharge;
}
someClass.Total = (someClass.LICodeValue + someClass.FUCodeValue);
//check for other values to calculate
//insert record to DB
}
else
{
//Insert into db after maipulation of values
}
}
}
isRecordBad = false;
}//END Using
Any clues would be greatly appreciated. Thank you in advance.

c# autocad sideload database binding xrefs

i am trying to bind xrefs in a sideload drawing database. the program is halting at this line ' if(!xNode.Database.Filename.Equals(NewDb.Filename))'. i am also receiving this error 'System.NullReferenceException: Object reference not set to an instance of an object.at XBind.RecursiveFileProcessor.ProcessFile(String path).' i've done some reaserch and found VB.NET code to attach a xref and tried to extrapolate that with no success. i'd appreciate someone pointing me in the right direction on this.
using (Database NewDb = new Database(false, true))
{
NewDb.ReadDwgFile(path, FileOpenMode.OpenForReadAndWriteNoShare, true, "");
NewDb.CloseInput(true);
using (Transaction tr = NewDb.TransactionManager.StartTransaction())
{
ObjectIdCollection xrefCollection = new ObjectIdCollection();
XrefGraph xg = NewDb.GetHostDwgXrefGraph(false);
int numOfNodes = xg.NumNodes;
for (int cnt = 0; cnt < xg.NumNodes; cnt++)
{
XrefGraphNode xNode = xg.GetXrefNode(cnt) as XrefGraphNode;
if (!xNode.Database.Filename.Equals(NewDb.Filename))
{
if (xNode.XrefStatus == XrefStatus.Resolved)
{
xrefCollection.Add(xNode.BlockTableRecordId);
}
}
}
if (xrefCollection.Count != 0)
{
NewDb.BindXrefs(xrefCollection, true);
}
tr.Commit();
}
NewDb.SaveAs(path, DwgVersion.Current);
}
Actually, this will work in-memory. Winslow North missing the following line of code after the CloseInput()...
NewDb.ResolveXrefs(true, false);
But also, you do not need the Transaction for this. It's not necessary. I created my own sample and tested it. It works. If you need me to post that, let me know. The problem was that the xNode had a null database due to the fact that the Xref was not resolved. You have to do that manually with the line above.
Don't believe this will work for in-memory database, you may try this approach, see a pice of it below:
[CommandMethod("CHX")]
public void ChangeXref()
{
var doc = Application.DocumentManager.MdiActiveDocument;
if (doc == null) return;
var ed = doc.Editor;
var db = doc.Database;
// Get the database associated with each xref in the
// drawing and change all of its circles to be dashed
using (var tr = db.TransactionManager.StartTransaction())
{
var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
var ms = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
// Loop through the contents of the modelspace
foreach (var id in ms)
{
// We only care about BlockReferences
var br = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
if (br != null)
{
// Check whether the associated BlockTableRecord is
// an external reference
var bd = (BlockTableRecord)tr.GetObject(br.BlockTableRecord, OpenMode.ForRead);
if (bd.IsFromExternalReference)
{
// If so, get its Database and call the function
// to change the linetype of its Circles
var xdb = bd.GetXrefDatabase(false);
if (xdb != null)
{
using (var xf = XrefFileLock.LockFile(xdb.XrefBlockId))
{
// Make sure the original symbols are loaded
xdb.RestoreOriginalXrefSymbols();
xdb.RestoreForwardingXrefSymbols();
}
}
}
}
}
tr.Commit();
}
}

How to refresh Formatting on Non-Calculated field and refresh Calculated fields in Fillable PDF form

I have a PDF Template file that I am trying to populate with contents of "MyDocument". All the fields populate fine but the problem is that the "Calculated" fields in my PDF are not refreshed nor is the formatting set on other fields. How do I make the calculated fields refresh and formatting to work using ITextSharp? (I do not care if I get a C# or VB.NET answer)
VB.NET:
Public Shared Sub Serialize(ByVal stmt As MyDocument, ByVal file As FileInfo)
Dim reader As New PdfReader(TemplateFilePath.FullName)
Dim pdfStamper As New PdfStamper(reader, New FileStream(file.FullName, FileMode.Open))
Try
With itsDaDetailFields
.MoveFirst()
While Not .EOF
Dim pdfFieldName As String = NsT(Of String)(!PDFFieldName, Nothing)
If Not String.IsNullOrEmpty(pdfFieldName) Then
Dim value As String = NsT(Of String)(stmt.GetValueFromPDFField(pdfFieldName), Nothing)
If Not String.IsNullOrEmpty(value) Then
pdfStamper.AcroFields.SetField(pdfFieldName, value)
End If
End If
.MoveNext()
End While
End With
Finally
pdfStamper.FormFlattening = False
reader.Close()
pdfStamper.Close()
End Try
End Sub
C#:
public static void Serialize(MyDocument stmt, FileInfo file)
{
PdfReader reader = new PdfReader(TemplateFilePath.FullName);
PdfStamper pdfStamper = new PdfStamper(reader, new FileStream(file.FullName, FileMode.Open));
try {
var _with1 = itsDaDetailFields;
_with1.MoveFirst();
while (!_with1.EOF) {
string pdfFieldName = NsT<string>(_with1["PDFFieldName"], null);
if (!string.IsNullOrEmpty(pdfFieldName)) {
string value = NsT<string>(stmt.GetValueFromPDFField(pdfFieldName), null);
if (!string.IsNullOrEmpty(value)) {
pdfStamper.AcroFields.SetField(pdfFieldName, value);
}
}
_with1.MoveNext();
}
} finally {
pdfStamper.FormFlattening = false;
reader.Close();
pdfStamper.Close();
}
}
So I figured out how to do it in .NET based on the following post using iText (the java version of ITextSharp - the procedure is just slightly different for .net). Feel free to read the following thread for a complete explanation and discussion of the same problem in iText:
http://itext-general.2136553.n4.nabble.com/Setting-acroform-value-via-iText-messes-with-acrofield-formating-td2167101.html
There are 2 ways to do it:
(1) Provide the display value like:
pdfStamper.AcroFields.SetField(pdfFieldName, value, <formatted value>)
as in:
pdfStamper.AcroFields.SetField(pdfFieldName, 1000, "1,000")
This wasn't optimal for me because I couldn't figure out programattically from my PDF file which textboxes were formatting their contents in which format. Some had slightly different formats (some had 2 decimal places, some had 0, some had many) so if you could keep track of how a textbox formats its data or if they all do the same thing then this might work. This also didn't fix the calculated fields problem, just seemed to fix the formatting problem.
(2) Provide javascript to "DIRTY" the value so it gets formatted & calculated:
My code turned into something like the following since I only needed to format numeric values but this can be expanded to handle other types (see discussion below).
Dim reader As New PdfReader(TemplateFilePath.FullName)
Dim pdfStamper As New PdfStamper(reader, New FileStream(file.FullName, FileMode.Open))
With pdfStamper.AcroFields
If IsNumeric(value) Then
Dim js As String = String.Format("var f = this.getField('{0}'); f.value = 1 * f.value;", pdfFieldName)
pdfStamper.JavaScript = js
End If
.SetField(pdfFieldName, value)
End With
reader.Close()
pdfStamper.Close()
So the trick is that you need to use JavaScript to get the value dirty, then Reader will apply the formatting. You can generalize this more and handle more value types based on the complete solution provided below (sorry it is in java but can be adapted to .net):
import java.io.IOException;
import java.util.ArrayList;
import com.lowagie.text.pdf.PRStream;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfObject;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfString;
import com.lowagie.text.pdf.AcroFields.Item;
public class AcroFieldJSScanner {
protected ArrayList<String> functions = null;
public void getFieldFunctions(Item item) throws IOException{
PdfDictionary dict;
for (int i = 0; i < item.size(); i++) {
dict = item.getMerged(i);
scanPdfDictionary(dict);
// dict = item.getWidget(i);
//
// scanPdfDictionary(dict);
}
}
protected void scanPdfDictionary(PdfDictionary dict) throws IOException{
PdfObject objJS = null;
String func = null;
objJS = dict.get(PdfName.JS);
if (dict.get(PdfName.S) != null && objJS != null && objJS.isString()){
PdfString strJS = (PdfString)objJS;
if (functions == null){
functions = new ArrayList<String>();
}
func = strJS.toString();
functions.add(func);
}else if (dict.get(PdfName.S) != null && objJS != null){
for(Object obj : dict.getKeys()){
PdfName pdfName = (PdfName)obj;
PdfObject pdfObj = dict.get(pdfName);
if (pdfObj.isIndirect()){
PdfObject pdfIndirectObject = PdfReader.getPdfObject(pdfObj);
func = new String(PdfReader.getStreamBytes((PRStream)pdfIndirectObject));
if (functions == null){
functions = new ArrayList<String>();
}
functions.add(func);
}else{
scanPdfObject(pdfObj);
}
}
}else{
for(Object obj : dict.getKeys()){
PdfName pdfName = (PdfName)obj;
PdfObject pdfObj = dict.get(pdfName);
scanPdfObject(pdfObj);
}
}
}
protected void scanPdfObject(PdfObject parentPdfObject) throws IOException{
if (parentPdfObject.isDictionary()){
scanPdfDictionary((PdfDictionary)parentPdfObject);
}else if (parentPdfObject.isIndirect()){
PdfObject pdfObject = PdfReader.getPdfObject(parentPdfObject);
scanPdfObject(pdfObject);
}
}
public ArrayList<String> getFunctions() {
return functions;
}
public String toString(){
StringBuilder sb = null;
if (getFunctions() != null){
sb = new StringBuilder();
for (int i =0; i< getFunctions().size();i++) {
sb.append(getFunctions().get(i)).append("\n");
}
}else{
return "No functions found";
}
return sb.toString();
}
}
And then if you know the javascript scripts that Adobe will call (using the above code) you know what type the data is so you can "DIRTY" the data. Here are some adobe data types and the javascript that is behind those data types:
public String getFieldFormat(Item item){
PdfDictionary aa = (PdfDictionary) item.getMerged(0).get(PdfName.AA);
if (null != aa)
{
PdfDictionary f = (PdfDictionary)PdfReader.getPdfObject(aa.get(PdfName.F));
if (null != f)
{
PdfString js = (PdfString)PdfReader.getPdfObject(f.get(PdfName.JS));
if (null != js)
{
String sScriptName = js.toString();
if (sScriptName.contains("AFNumber_Format"))
System.out.println("Format : Number");
else if (sScriptName.contains("AFDate_Format"))
System.out.println("Format : Date");
else if (sScriptName.contains("AFTime_Format"))
System.out.println("Format : Time");
else if (sScriptName.contains("AFSpecial_Format"))
System.out.println("Format : Special");
else if (sScriptName.contains("AFPercent_Format"))
System.out.println("Format : Percent");
else
System.out.println("Format : Custom");;
System.out.println("JS: ");
System.out.println(js);
}
}
}
}
In my case, I found out that:
Using FormFlattening prevents the javascript from updating the field
Setting the field using SetField does not trigger the formatting.
So I had to change the javascript to actually write the complete value instead of just dirtying it. Like so:
JS &= String.Format("var f = this.getField('{0}'); f.value = '{1}';",
FieldName.Key, NewFieldValue)
I append the javascript code for each field into the string JS, and then I call pdfStamper.Javascript = JS at the end.

Categories