How to create virtual XML for ZUGFeRD Invoices - perl

I try to create a PDF/A-3b file which contains an embedded XML-File to be ZUGFeRD conform. I use Perl and PDFLib for this purpose. The PDFLib Documentation out there is just for Java and PHP. Creating the PDF works fine, but the XML part is my problem.
So how can i create a pvf from xml and join this to my pdf?
This is what PDFLib recommends in Java:
// Place XML stream in a virtual PVF file
String pvf_name = "/pvf/ZUGFeRD-invoice.xml";
byte[] xml_bytes = xml_string.getBytes("UTF-8");
p.create_pvf(pvf_name, xml_bytes, "");
// Create file attachment (asset) from PVF file
int xml_asset = p.load_asset("Attachment", pvf_name,
"mimetype=text/xml description={ZUGFeRD invoice in XML format} "
+ "relationship=Alternative documentattachment=true");
// Associate file attachment with the document
p.end_document("associatedfiles={" + xml_asset + "}");
So I thought, take the example and fit it to perl:
my $xmldata = read_file($xmlfile, binmode => ':utf8'); #I use example xml at the moment
my $pvf_xml = "/pvf/ZUGFeRD-invoice.xml";
PDF_create_pvf($pdf, $pvf_xml, $xmldata, ""); #because no OOP i need to call it this way (works with all other PDF Functions)
my $xml_invoice = PDF_load_asset("Attachment", $pvf_xml, "mimetype=text/xml "
."description={Rechnungsdaten im Zugferd-Xml-Format} "
."relationship=Alternative documentattachment=true");
PDF_end_document($pdf, "associatedfiles={".$xml_invoice."}");
In PHP examples it's also not needed to convert to ByteArray after reading xml. Further tried it with unpack but don't seem to be the problem.
If I call my script I'm just getting:
Usage: load_asset(type, filename, optlist); at signatur_test.pl line
41.
I think the problem is that pvf_xml isn't created the line before.
Anyone did this before and no how to solve this?

Arg, i was just missing the PDF-Handle in the load_asset method:
my $xml_invoice = PDF_load_asset($pdf, "Attachment", $pvf_xml, "mimetype=text/xml "
."description={Rechnungsdaten im Zugferd-Xml-Format} "
."relationship=Alternative documentattachment=true");
This way it works.

Related

Protractor - Create a txt file as report with the "Expect..." result

I'm trying to create a report for my scenario, I want to execute some validations and add the retults in a string, then, write this string in a TXT file (for each validation I would like to add the result and execute again till the last item), something like this:
it ("Perform the loop to search for different strings", function()
{
browser.waitForAngularEnabled(false);
browser.get("http://WebSite.es");
//strings[] contains 57 strings inside the json file
for (var i = 0; i == jsonfile.strings.length ; ++i)
{
var valuetoInput = json.Strings[i];
var writeInFile;
browser.wait;
httpGet("http://website.es/search/offers/list/"+valuetoInput+"?page=1&pages=3&limit=20").then(function(result) {
writeInFile = writeInFile + "Validation for String: "+ json.Strings[i] + " Results is: " + expect(result.statusCode).toBe(200) + "\n";
});
if (i == jsonfile.strings.length)
{
console.log("Executions finished");
var fs = require('fs');
var outputFilename = "Output.txt";
fs.writeFile(outputFilename, "Validation of Get requests with each string:\n " + writeInFile, function(err) {
if(err)
{
console.log(err);
}
else {
console.log("File saved to " + outputFilename);
}
});
}
};
});
But when I check my file I only get the first row writen in the way I want and nothing else, could you please let me know what am I doing wrong?
*The validation works properly in the screen for each of string in my file used as data base
**I'm a newbie with protractor
Thank you a lot!!
writeFile documentation
Asynchronously writes data to a file, replacing the file if it already
exists
You are overwriting the file every time, which is why it only has 1 line.
The easiest way would probably (my opinion) be appendFile. It writes to a file without overwriting existing data and will also create the file if it doesnt exist in the first place.
You could also re-read that log file, store that data in a variable, and re-write to that file with the old AND new data included in it. You could also create a writeStream etc.
There are quite a few ways to go about it and plenty of other answers
on SO specifically on those functions that can provide more info.
Node.js Write a line into a .txt file
Node.js read and write file lines
Final note, if you are using Jasmine you can also create a custom jasmine reporter. They have methods that contain exactly what you want (status Pass/Fail, actual vs expected values etc) and it's fairly easy to set up with Protractor

How to edit pasted content using the Open XML SDK

I have a custom template in which I'd like to control (as best I can) the types of content that can exist in a document. To that end, I disable controls, and I also intercept pastes to remove some of those content types, e.g. charts. I am aware that this content can also be drag-and-dropped, so I also check for it later, but I'd prefer to stop or warn the user as soon as possible.
I have tried a few strategies:
RTF manipulation
Open XML manipulation
RTF manipulation is so far working fairly well, but I'd really prefer to use Open XML as I expect it to be more useful in the future. I just can't get it working.
Open XML Manipulation
The wonderfully-undocumented (as far as I can tell) "Embed Source" appears to contain a compound document object, which I can use to modify the copied content using the Open XML SDK. But I have been unable to put the modified content back into an object that lets it be pasted correctly.
The modification part seems to work fine. I can see, if I save the modified content to a temporary .docx file, that the changes are being made correctly. It's the return to the clipboard that seems to be giving me trouble.
I have tried assigning just the Embed Source object back to the clipboard (so that the other types such as RTF get wiped out), and in this case nothing at all gets pasted. I've also tried re-assigning the Embed Source object back to the clipboard's data object, so that the remaining data types are still there (but with mismatched content, probably), which results in an empty embedded document getting pasted.
Here's a sample of what I'm doing with Open XML:
using OpenMcdf;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
...
object dataObj = Forms.Clipboard.GetDataObject();
object embedSrcObj = dateObj.GetData("Embed Source");
if (embedSrcObj is Stream)
{
// read it with OpenMCDF
Stream stream = embedSrcObj as Stream;
CompoundFile cf = new CompoundFile(stream);
CFStream cfs = cf.RootStorage.GetStream("package");
byte[] bytes = cfs.GetData();
string savedDoc = Path.GetTempFileName() + ".docx";
File.WriteAllBytes(savedDoc, bytes);
// And then use the OpenXML SDK to read/edit the document:
using (WordprocessingDocument openDoc = WordprocessingDocument.Open(savedDoc, true))
{
OpenXmlElement body = openDoc.MainDocumentPart.RootElement.ChildElements[0];
foreach (OpenXmlElement ele in body.ChildElements)
{
if (ele is Paragraph)
{
Paragraph para = (Paragraph)ele;
if (para.ParagraphProperties != null && para.ParagraphProperties.ParagraphStyleId != null)
{
string styleName = para.ParagraphProperties.ParagraphStyleId.Val;
Run run = para.LastChild as Run; // I know I'm assuming things here but it's sufficient for a test case
run.RunProperties = new RunProperties();
run.RunProperties.AppendChild(new DocumentFormat.OpenXml.Wordprocessing.Text("test"));
}
}
// etc.
}
openDoc.MainDocumentPart.Document.Save(); // I think this is redundant in later versions than what I'm using
}
// repackage the document
bytes = File.ReadAllBytes(savedDoc);
cf.RootStorage.Delete("Package");
cfs = cf.RootStorage.AddStream("Package");
cfs.Append(bytes);
MemoryStream ms = new MemoryStream();
cf.Save(ms);
ms.Position = 0;
dataObj.SetData("Embed Source", ms);
// or,
// Clipboard.SetData("Embed Source", ms);
}
Question
What am I doing wrong? Is this just a bad/unworkable approach?

How to print a file with Jscript

Goal
I want to print a file via a PDF printer which isn't the default printer. I was able to temporary change the normal printer to the PDF printer.
Problem
But I don't know how to print a .doc, .txt or .xls via Jscript. Also, I can't find a way to save the default printer name so I can switch back after I've printed the file.
Jscript code
var objShell = new ActiveXObject("Shell.Application");
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
try {
var PDFCreatorQueue = new ActiveXObject("PDFCreatorBeta.JobQueue");
PDFCreatorQueue.Initialize();
var sourceFile = WScript.Arguments(0)
var sourceFolder = objFSO.GetParentFolderName(sourceFile)
var sourceName = objFSO.GetBaseName(sourceFile)
var targetFile = sourceFolder + "\\" + sourceName + ".pdf"
//HERE GOES THE COMMAND TO SAVE THE CURRENT DEFAULT PRINTER NAME TO A TEMP VARIABLE
objNet.SetDefaultPrinter("PDFCreator");
//HERE GOES THE PRINT COMMAND WHICH I DON'T KNOW
// HERE GOES THE COMMAND TO CHANGE BACK TO THE OLD DEFAULT PRINTER
if(!PDFCreatorQueue.WaitForJob(3)) {
WScript.Echo("The print job did not reach the queue within " + 3 + " seconds");
}
else {
var job = PDFCreatorQueue.NextJob;
job.SetProfileByGUID("DefaultGuid");
job.ConvertTo(targetFile);
if(!job.IsFinished || !job.IsSuccessful) {
WScript.Echo("Could not convert the file: " + targetFile);
}
}
PDFCreatorQueue.ReleaseCom();
}
catch(e) {
WScript.Echo(e.message);
PDFCreatorQueue.ReleaseCom();
}
Use the ShellFolderItem.InvokeVerbEx() function. The JScript example code in the MSDN article shows how to use it. Make the first argument "print" and the second argument the name of the printer. So you can remove the code that tinkers with the default printer.
Printing web page from js is quite easy, you could use window.print() method over an iFrame ( this works only with file format wich can be displaied into a web page so it doesn't work with .doc extension)
<iframe id="textfile" src="text.txt"></iframe>
<button onclick="print()">Print</button>
<script type="text/javascript">
function print() {
var iframe = document.getElementById('textfile');
iframe.contentWindow.print();
}
</script>
These will show you a message box to select what printer you want to use a so on.
What are you asking for seems to be silent printing but it isn't standarized over all the broswer.
P.S. I think that isn't a good idea to use the printer to save this file to pdf, I think taht you could look at jsPDF (a js tools to create pdf) or you should consider to make the pdf generation serverside.

Problem while configuring the file Delimeter("\t") in app.config(C#3.0)

In my app.config file I made the setting like the following
<add key = "Delimeter" value ="\t"/>
Now while accessing the above from the program by using the below code
string delimeter = ConfigurationManager.AppSettings["FileDelimeter"].ToString();
StreamWriter streamWriter = null;
streamWriter = new StreamWriter(fs);
streamWriter.BaseStream.Seek(0, SeekOrigin.End);
Enumerable
.Range(0, outData.Length)
.ToList().ForEach(i => streamWriter.Write(outData[i].ToString() + delimiter));
streamWriter.WriteLine();
streamWriter.Flush();
I am getting the output as
18804\t20100326\t5.59975381254617\t
18804\t20100326\t1.82599797249479\t
But if I directly use "\t" in the delimeter variable I am getting the correct output
18804 20100326 5.59975381254617
18804 20100326 1.82599797249479
I found that while I am specifying the "\t" in the config file, and while reading it into
the delimeter variable, it is becoming "\\t" which is the problem.
I even tried with but with no luck.
I am using C#3.0.
Need help
You need to use the XML entity that represents tab, which I believe is rather than the C# representation (which is "\t" as you already know).
<add key="Delimeter" value=" "/>
Or you could always just take the easy way out:
// allow for <add key="Delimeter" value="\t"/>
if (delimiter == #"\t")
delimiter = "\t";

Custom clipboard data format accross RDC (.NET)

I'm trying to copy a custom object from a RDC window into host (my local) machine. It fails.
Here's the code that i'm using to 1) copy and 2) paste:
1) Remote (client running on Windows XP accessed via RDC):
//copy entry
IDataObject ido = new DataObject();
XmlSerializer x = new XmlSerializer(typeof(EntryForClipboard));
StringWriter sw = new StringWriter();
x.Serialize(sw, new EntryForClipboard(entry));
ido.SetData(typeof(EntryForClipboard).FullName, sw.ToString());
Clipboard.SetDataObject(ido, true);
2) Local (client running on local Windows XP x64 workstation):
//paste entry
IDataObject ido = Clipboard.GetDataObject();
DataFormats.Format cdf = DataFormats.GetFormat(typeof(EntryForClipboard).FullName);
if (ido.GetDataPresent(cdf.Name)) //<- this always returns false
{
//can never get here!
XmlSerializer x = new XmlSerializer(typeof(EntryForClipboard));
string xml = (string)ido.GetData(cdf.Name);
StringReader sr = new StringReader(xml);
EntryForClipboard data = (EntryForClipboard)x.Deserialize(sr);
}
It works perfectly on the same machine though.
Any hints?
There are a couple of things you could look into:
Are you sure the serialization of the object truely converts it into XML? Perhaps the outputted XML have references to your memory space? Try looking at the text of the XML to see.
If you really have a serialized XML version of the object, why not store the value as plain-vanilla text and not using typeof(EntryForClipboard) ? Something like:
XmlSerializer x = new XmlSerializer(typeof(EntryForClipboard));
StringWriter sw = new StringWriter();
x.Serialize(sw, new EntryForClipboard(entry));
Clipboard.SetText(sw.ToString(), TextDataFormat.UnicodeText);
And then, all you'd have to do in the client-program is check if the text in the clipboard can be de-serialized back into your object.
Ok, found what the issue was.
Custom format names get truncated to 16 characters when copying over RDC using custom format.
In the line
ido.SetData(typeof(EntryForClipboard).FullName, sw.ToString());
the format name was quite long.
When i was receiving the copied data on the host machine the formats available had my custom format, but truncated to 16 characters.
IDataObject ido = Clipboard.GetDataObject();
ido.GetFormats(); //used to see available formats.
So i just used a shorter format name:
//to copy
ido.SetData("MyFormat", sw.ToString());
...
//to paste
DataFormats.Format cdf = DataFormats.GetFormat("MyFormat");
if (ido.GetDataPresent(cdf.Name)) {
//this not works
...