Renaming Checkbox Fields with iTextSharp - c#

im am trying to rename fields in itext. I can successfully rename textfields, but i am getting errors renaming checkboxfields. Here is the code so far:
try {
pdfReader = new PdfReader(s_pdfread);
} catch (Exception ex) {
Console.WriteLine("Can't create Filestream from " + s_pdfread);
Console.WriteLine(ex.Message);
ShowHelp();
Environment.Exit(-1);
}
List <renameJob> jobsToDo = new List<renameJob> ();
try {
using (FileStream fs = new FileStream(s_pdfwrite, FileMode.Create)) {
PdfStamper stamper = new PdfStamper(pdfReader, fs);
AcroFields fields = stamper.AcroFields;
foreach (var field in stamper.AcroFields.Fields) {
Console.WriteLine("fieldKeySRC: " + field.Key);
string newFieldname = field.Key.Replace(s_searchFor, s_replaceWith);
newFieldname = newFieldname.Replace(".", "");
// special name for checkbox
/*switch (stamper.AcroFields.GetFieldType(field.Key)) {
case AcroFields.FIELD_TYPE_CHECKBOX:
newFieldname = newFieldname + ".0";
break;
} planned for future. In finaly version i need to add this .0 */
if (field.Key != newFieldname) {
renameJob job = new renameJob();
job.oldName = field.Key;
job.newName = newFieldname;
jobsToDo.Add(job);
}
}
foreach (renameJob job in jobsToDo) {
if (fields.RenameField(job.oldName, job.newName) == true) {
Console.WriteLine("renameField: " + job.oldName + " to " + job.newName);
}
else {
Console.WriteLine("!!! failed: renameField: " + job.oldName + " to " + job.newName);
}
}
stamper.Close();
why does this fail for checkboxes and work fine for textboxes.... ?

As mkl explains, the "." in a field name is NOT part of the name, it's there to indicate a hierarchy.
Suppose you have a parent field named "parent" with two kids "kid1" and "kid2". In this case, the fully qualified names of the kid fields are "parent.kid1" and "parent.kid2".
With iText, you can rename fields, but you can't change the hierarchy. For instance: you can change the fully qualified name "mybox" into "hisbox", you can change the fully qualified name "parent.kid1" into "parent.child1" and "parent.kid2" into "parent.child2".
IT DOESN'T MATTER if these fields are text fields, checkbox fields, or any other type of field!
In other words: your question is wrong! The problem you have, is that you're not trying to rename a field. Instead you're trying to change "parent.kid1" into "parentkid1" and "parent.kid2" into "parentkid2". That's not the same as renaming! That's removing the hierarchy, changing a parent and two kids into two parentless kids! You can't use the rename function to do this.
If you really want to throw away the hierarchy, you need to add two new fields "parentkid1" and "parentkid2" that copy the properties of "parent.kid1" and "parent.kid2". Once you have these copies, you need to remove "parent", "kid1" and "kid2".
That's much more work. I'd advise against it, as it's an error-prone operation.

Related

The number of frames tag appears in the dataset but is not in the DICOMDIR C#

I add dicom files using the AddFile(dicomFile,name) method but the number of frames tag does not appear.
var sourcePath = Path.Combine(tempDirectory, "DICOM", $"PATIENT{i + 1}", $"STUDY{j + 1}", $"SERIES{k + 1}", $"SUBSERIES{l + 1}");
var dicomDir = new DicomDirectory { AutoValidate = false };
foreach (var file in new DirectoryInfo(tempDirectory).GetFiles("*.*", SearchOption.AllDirectories))
{
try
{
var dicomFile = DicomFile.Open(file.FullName);
if (dicomFile != null)
{
var referenceField = file.FullName.Replace(tempDirectory, string.Empty).Trim('\\');
dicomDir.AddFile(dicomFile, referenceField);
}
}
catch (Exception ex)
{
Log.Error(ex, ex.Message);
}
}
var dicomDirPath = Path.Combine(tempDirectory, "DICOMDIR");
dicomDir.Save(dicomDirPath);
resultDirectories.Add(dicomDirPath);
I also tried the addorupdate method but it doesn't work.
I use the fo-dicom library 4.0.7
When building a DICOMDIR with fo-dicom by iterative calling AddFile for each file, then you will get a DICOMDIR with all the required DicomTags. But of course there are a lot of tags that are optional and you can add them yourself.
The method AddFile returns an instance of type DicomDirectoryEntry, which gives you a reference to the patient record entry, the study record entry, the series record entry and the instance record entry. There you can add as many additional optional data that you wish. In your case it would look like
[...]
if (dicomFile != null)
{
var referenceField = file.FullName.Replace(tempDirectory, string.Empty).Trim('\\');
var entries = dicomDir.AddFile(dicomFile, referenceField);
// now you can add some additional data.
// but before adding values, make sure that those values are available
// in your original DicomFile to avoid NullReferenceExceptions.
if (dicomFile.Dataset.Contains(DicomTag.NumberOfFrames))
{
entries.InstanceRecord.AddOrUpdate(DicomTag.NumberOfFrames, dicomFile.Dataset.GetSingleValue<int>(DicomTag.NumberOfFrames));
}
}

Can't use saved information to load into an array

I've managed to save some information into a file. I want to be able to read that information and use it to create an item
public void Save()
{
StreamWriter writer;
writer = new StreamWriter("PlayerInventory.txt");
for (int i = 0; i < playerInventory.Length; i++)
{
if (playerInventory[i] != null)
{
if (playerInventory[i] is Sheild sheild)
{
writer.WriteLine("Sheild:" + sheild.Defence + "," + sheild.name + "," + sheild.description + "," + sheild.cost);
}
}
}
else
{}
}
I started using this code to load it but i don't know how to finish the code. Right now its split but i don't know how to assign it to anything.
For example sheild:20,Shield,this is a shield,100
Right now that's split into
20,
Shield,
this is a shield,
100
But I don't know who to assign those stuff to an array
public void Load()
{
StreamReader reader;
reader = new StreamReader("PlayerInventory.txt");
string line;
string[] currentLineData;
while (true)
{
try
{
line = reader.ReadLine();
if (line == null)
{
break;
}
if (line.Contains("Sheild"))
{
line.Replace("Sheild:", "");
currentLineData = line.Split(',');
}
}
catch
{
break;
}
}
reader.Close();}
it looks like you are designing a game.
If I were setting up an inventory that included a shield with stats, I'd look at creating a complex data type, using a "struct" for an inventory item.
Then I'd create an array of the inventory struct items, with one for each inventory slot - before loading my file in.
This could hold all the different info you need for any item that can be stored in inventory like Item Name, defense strength, attack strength etc.
Then, when you load your file in, all the right stuff will be in places with nice names etc. for you to easily find them later.
If you need an extra stat, add a row to your struct definition, then add a column to your file and you are ready to go.

Compare two word document in c#

I have a problem. I need to compare word document. Text and format in c# and i found a third party library to view and process the document and it is Devexpress. So i downloaded the trial to check if the problem can be solved with this
Example i have two word document
1: This is a text example
This is not a text example
In the text above the difference is only the word not
My problem is how can i check the difference including the format?
So far this is my code for iterating the contents of the Document
public void CompareEpub(string word)
{
try
{
using (DevExpress.XtraRichEdit.RichEditDocumentServer srv = new DevExpress.XtraRichEdit.RichEditDocumentServer())
{
srv.LoadDocument(word);
MyIterator visitor = new MyIterator();
DocumentIterator iterator = new DocumentIterator(srv.Document, true);
while (iterator.MoveNext())
{
iterator.Current.Accept(visitor);
}
foreach (var item in visitor.ListOfText)
{
Debug.WriteLine("text: " + item.Text + " b: " + item.IsBold + " u: " + item.IsUnderline + " i: " + item.IsUnderline);
}
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
Debug.WriteLine(ex.StackTrace);
throw ex;
}
}
public class MyIterator : DocumentVisitorBase
{
public List<Model.HtmlContent> ListOfText { get; }
public MyIterator()
{
ListOfText= new List<Model.HtmlContent>();
}
public override void Visit(DocumentText text)
{
var m = new Model.HtmlContent
{
Text = text.Text,
IsBold = text.TextProperties.FontBold,
IsItalic = text.TextProperties.FontItalic,
IsUnderline = text.TextProperties.UnderlineWordsOnly
};
ListOfText.Add(m);
}
}
With the code above i can navigate to the text and its format. But how can i use this as a text compare?
If I'm going to create a two list for each document to compare.
How can i compare it?
If i'm going to compare the text in with another list. Compare it in loop.
I will be receiving it as only two words are equal.
Can help me with this. Or just provide an idea how i can make it work.
I didn't post in the devexpress forum because i feel that this is a problem with how i will be able to do it. And not a problem with the trial or the control i've been using. And i also found out that the control doesn't have a functionality to compare text. Like the one with Microsoft word.
Thank you.
Update:
Desired output
This is (not) a text example
The text inside the () means it is not found in the first document
The output i want is like the output of Diff Match Patch
https://github.com/pocketberserker/Diff.Match.Patch
But i can't implement the code for checking the format.

PdfSharp, Updating Metadata in C# Error

I am using the PdfSharp reference library to attempt to add functionality to my program that adds metadata tags. I am able to successfully add metadata tags to a document, but I am having an issue with updating the tags on existing custom properties. Whenever I attempt to use my method to update the custom properties, I receive the following exception:
"'System.Collections.Generic.KeyValuePair' does not contain a definition for 'Name'."
Could you guys tell me if I am coding the if statement in the foreach loop below to correctly loop through all of the custom elements in the PDF document to see if it exists and needs to be updated? Thanks.
public void AddMetaDataPDF(string property, string propertyValue, string
path)
{
PdfDocument document = PdfReader.Open(path);
bool propertyFound = false;
try {
dynamic properties = document.Info.Elements;
foreach(dynamic p in properties)
{
//Check to see if the property exists. If it does, update
value.
if(string.Equals(p.Name, property,
StringComparison.InvariantCultureIgnoreCase))
{
document.Info.Elements.SetValue("/" + property, new
PdfString(propertyValue));
}
}
// the property doesn't exist so add it
if(!propertyFound)
{
document.Info.Elements.Add(new KeyValuePair<String, PdfItem>
("/"+ property, new PdfString(propertyValue)));
}
}
catch (Exception ex)
{
MessageBox.Show(path + "\n" + ex.Message);
document.Close();
}
finally
{
if(document != null)
{
document.Save(path);
document.Close();
}
}
}
I didn't try your code but a common issue when working with this library is that you need to add a slash before the name of the property for it to be found. The code below will make the trick.
PdfDocument document = PdfReader.Open(path);
var properties = document.Info.Elements;
if (properties.ContainsKey("/" + propertyName))
{
properties.SetValue("/" + propertyName, new PdfString(propertyValue));
}
else
{
properties.Add(new KeyValuePair<String, PdfItem>("/" + propertyName, new PdfString(propertyValue)));
}
document.Save(path);
document.Close();
Also the PDF file shouldn't be write protected. Otherwise you need to use a tool for unlocking the file before calling PdfSharp.

Can ConfigurationManager retain XML comments on Save()?

I've written a small utility that allows me to change a simple AppSetting for another application's App.config file, and then save the changes:
//save a backup copy first.
var cfg = ConfigurationManager.OpenExeConfiguration(pathToExeFile);
cfg.SaveAs(cfg.FilePath + "." + DateTime.Now.ToFileTime() + ".bak");
//reopen the original config again and update it.
cfg = ConfigurationManager.OpenExeConfiguration(pathToExeFile);
var setting = cfg.AppSettings.Settings[keyName];
setting.Value = newValue;
//save the changed configuration.
cfg.Save(ConfigurationSaveMode.Full);
This works well, except for one side effect. The newly saved .config file loses all the original XML comments, but only within the AppSettings area. Is it possible to to retain XML comments from the original configuration file AppSettings area?
Here's a pastebin of the full source if you'd like to quickly compile and run it.
I jumped into Reflector.Net and looked at the decompiled source for this class. The short answer is no, it will not retain the comments. The way Microsoft wrote the class is to generate an XML document from the properties on the configuration class. Since the comments don't show up in the configuration class, they don't make it back into the XML.
And what makes this worse is that Microsoft sealed all of these classes so you can't derive a new class and insert your own implementation. Your only option is to move the comments outside of the AppSettings section or use XmlDocument or XDocument classes to parse the config files instead.
Sorry. This is an edge case that Microsoft just didn't plan for.
Here is a sample function that you could use to save the comments. It allows you to edit one key/value pair at a time. I've also added some stuff to format the file nicely based on the way I commonly use the files (You could easily remove that if you want). I hope this might help someone else in the future.
public static bool setConfigValue(Configuration config, string key, string val, out string errorMsg) {
try {
errorMsg = null;
string filename = config.FilePath;
//Load the config file as an XDocument
XDocument document = XDocument.Load(filename, LoadOptions.PreserveWhitespace);
if(document.Root == null) {
errorMsg = "Document was null for XDocument load.";
return false;
}
XElement appSettings = document.Root.Element("appSettings");
if(appSettings == null) {
appSettings = new XElement("appSettings");
document.Root.Add(appSettings);
}
XElement appSetting = appSettings.Elements("add").FirstOrDefault(x => x.Attribute("key").Value == key);
if (appSetting == null) {
//Create the new appSetting
appSettings.Add(new XElement("add", new XAttribute("key", key), new XAttribute("value", val)));
}
else {
//Update the current appSetting
appSetting.Attribute("value").Value = val;
}
//Format the appSetting section
XNode lastElement = null;
foreach(var elm in appSettings.DescendantNodes()) {
if(elm.NodeType == System.Xml.XmlNodeType.Text) {
if(lastElement?.NodeType == System.Xml.XmlNodeType.Element && elm.NextNode?.NodeType == System.Xml.XmlNodeType.Comment) {
//Any time the last node was an element and the next is a comment add two new lines.
((XText)elm).Value = "\n\n\t\t";
}
else {
((XText)elm).Value = "\n\t\t";
}
}
lastElement = elm;
}
//Make sure the end tag for appSettings is on a new line.
var lastNode = appSettings.DescendantNodes().Last();
if (lastNode.NodeType == System.Xml.XmlNodeType.Text) {
((XText)lastNode).Value = "\n\t";
}
else {
appSettings.Add(new XText("\n\t"));
}
//Save the changes to the config file.
document.Save(filename, SaveOptions.DisableFormatting);
return true;
}
catch (Exception ex) {
errorMsg = "There was an exception while trying to update the config value for '" + key + "' with value '" + val + "' : " + ex.ToString();
return false;
}
}
If comments are critical, it might just be that your only option is to read & save the file manually (via XmlDocument or the new Linq-related API). If however those comments are not critical, I would either let them go or maybe consider embedding them as (albeit redundant) data elements.

Categories