Background
I have been using iTextSharp for a short while now. I have created a pdf document with two signable PdfFormFields. If I open the pdf document I can manually sign each field manually. I want this to be done through iTextSharp.
I am currently retrieving the certificate from the X509Store. Up until this point, I could figure out.
Question
Can someone please show me how I can use this X509Certificate2
to sign an already existing signature AcroField.
References
The following references were visited and I couldn't find the answer I was looking for.
Signing a pdf document
This link got me the closest I believe, but several of the lines used were invalid and I do not know if I can fix it by including some other libraries.
https://www.dotnetportal.cz/blogy/15/Null-Reference-Exception/5250/Digitalni-podepisovani-PDF-souboru-v-C-cast-2
Everything you need:
appearance.SetVisibleSignature("---> ExistSignatureName <-----");
MakeSignature.SignDetached(appearance, pks, chain, null, null, null, 0, CryptoStandard.CMS);
Full Code:
private static void SignPdf(string filename, string folderPdf, string pathToNewSignFile, string pathToCerts, string nameCert, string passCert)
{
var pathToCert = GetFullNameFile(pathToCerts, nameCert); //Oh.. I did not know about the Path.Combine function.
if (!File.Exists(pathToCert))
{
logger.Error("Certificate not exist " + pathToCert);
return;
}
var pass = passCert.ToCharArray();
FileStream fs;
try
{
fs = new FileStream(pathToCert, FileMode.Open);
}
catch (Exception ex)
{
logger.Error(ex, "Could not open cert" + pathToCert);
return;
}
var store = new Pkcs12Store(fs, pass);
fs.Close();
var alias = "";
// searching for private key
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate) {
alias = al;
break;
}
var pk = store.GetKey(alias);
ICollection<X509Certificate> chain = store.GetCertificateChain(alias).Select(c => c.Certificate).ToList();
var parameters = pk.Key as RsaPrivateCrtKeyParameters;
var pathPdf = GetFullNameFile(folderPdf, filename); //Oh.. I did not know about the Path.Combine function.
var pathToSigPdf = GetFullNameFile(pathToNewSignFile, filename);
if (!File.Exists(pathPdf))
{
logger.Error("Could not open file" + pathPdf + " File not exist");
return;
}
var reader = new PdfReader(pathPdf);
FileStream fileStreamSigPdf;
try
{
fileStreamSigPdf = new FileStream(pathToSigPdf, FileMode.Create);
}
catch (Exception ex)
{
logger.Error(ex, "Could not create file" + pathToSigPdf);
return;
}
var stamper = PdfStamper.CreateSignature(reader, fileStreamSigPdf, '\0', null, true);
var appearance = stamper.SignatureAppearance;
appearance.Reason = "Утверждено";
appearance.SetVisibleSignature("---> ExistSignatureName <-----");
IExternalSignature pks = new PrivateKeySignature(parameters, DigestAlgorithms.SHA256);
MakeSignature.SignDetached(appearance, pks, chain, null, null, null, 0, CryptoStandard.CMS);
fileStreamSigPdf.Close();
reader.Close();
stamper.Close();
logger.Info("Signed successfully " + filename);
}
We have a web server in our company.
1. Get the hash field
2. With the help of the crypto plugin, we sign the hash
3. Insert the signature in the field
Step 1.
public string PrepareSignatureAndGetHash()
{
var hash = string.Empty;
using (var reader = new PdfReader("PathTemplate"))
{
using (var fileStream = File.OpenWrite("PathToTemp"))
{
using (var stamper = PdfStamper.CreateSignature(reader, fileStream, '0', null, true))
{
var signatureAppearance = stamper.SignatureAppearance;
signatureAppearance.SetVisibleSignature("ExistSignatureName");
IExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
signatureAppearance.Reason = "Sig";
signatureAppearance.Layer2Text = "Super SIG";
signatureAppearance.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
MakeSignature.SignExternalContainer(signatureAppearance, external, 8192);
using (var contentStream = signatureAppearance.GetRangeStream())
{
hash = string.Join(string.Empty, SHA1.Create().ComputeHash(contentStream).Select(x => x.ToString("X2")));
}
}
}
}
return hash;
}
Step 2.
public void SigSignature(string base64String)
{
using (var reader = new PdfReader("PathToTemp"))
{
using (var fileStream = File.OpenWrite("PathToSig"))
{
var byteSig = Convert.FromBase64String(base64String);
IExternalSignatureContainer external = new MfuaExternalSignatureContainer(byteSig);
MakeSignature.SignDeferred(reader, "ExistSignatureName", fileStream, external);
}
}
}
private class MfuaExternalSignatureContainer : IExternalSignatureContainer
{
private readonly byte[] _signedBytes;
public MfuaExternalSignatureContainer(byte[] signedBytes)
{
_signedBytes = signedBytes;
}
public byte[] Sign(Stream data)
{
return _signedBytes;
}
public void ModifySigningDictionary(PdfDictionary signDic)
{
}
}
Related
I am using iTextSharp library for digital signature. In my PDF file having a QR code and once we applied signature the QR code became a gray color.
The figure (A) shows a original PDF and figure (B) shows the PDF file after digital signature.
following is the code to apply the signature. Please help me, why the QR code became gray color.
public void Sign(PDFSignatureAP sigAP, bool encrypt, PdfEncryption Enc)
{
PdfReader reader = new PdfReader(this.inputPDF);
reader.RemoveUsageRights();
PdfStamper st;
if (this.myCert == null) //No signature just write meta-data and quit
{
st = new PdfStamper(reader, new FileStream(this.outputPDF,
FileMode.Create,FileAccess.Write));
}
else
{
st = PdfStamper.CreateSignature(reader, new FileStream(this.outputPDF, FileMode.Create,
FileAccess.Write), '\0', null, sigAP.Multi);
}
if (encrypt && Enc != null) Enc.Encrypt(st);
st.MoreInfo = this.metadata.getMetaData();
st.XmpMetadata = this.metadata.getStreamedMetaData();
if (this.myCert == null) //No signature just write meta-data and quit
{
st.Close();
return;
}
var pass = new SecureString();
char[] array = myCert.Password.ToCharArray();
foreach (char ch in array)
{
pass.AppendChar(ch);
}
var privateKey = myCert.SCertificate.PrivateKey as RSACryptoServiceProvider;
CspParameters cspParameters = new CspParameters(privateKey.CspKeyContainerInfo.ProviderType,
privateKey.CspKeyContainerInfo.ProviderName,
privateKey.CspKeyContainerInfo.KeyContainerName,
new System.Security.AccessControl.CryptoKeySecurity(),
pass);
RSACryptoServiceProvider rsaCsp;
if (string.IsNullOrEmpty(myCert.Path))
rsaCsp = new RSACryptoServiceProvider(cspParameters);
PdfSignatureAppearance sap = st.SignatureAppearance;
sap.Reason = sigAP.SigReason;
sap.Contact = sigAP.SigContact;
sap.Location = sigAP.SigLocation;
if (sigAP.Visible)
{
iTextSharp.text.Rectangle rect = st.Reader.GetPageSize(sigAP.Page);
sap.Image = sigAP.RawData == null ? null :
iTextSharp.text.Image.GetInstance(sigAP.RawData);
if(!string.IsNullOrEmpty(sigAP.CustomText))
sap.Layer2Text = sigAP.CustomText + Environment.NewLine + "Date :" + DateTime.Now;
sap.Acro6Layers = false;
sap.Layer4Text = PdfSignatureAppearance.questionMark;
sap.SignatureRenderingMode = PdfSignatureAppearance.RenderingMode.DESCRIPTION;
sap.SetVisibleSignature(new iTextSharp.text.Rectangle(sigAP.SigX, sigAP.SigY, sigAP.SigX + sigAP.SigW, sigAP.SigY + sigAP.SigH), sigAP.Page, "Signature");
}
IExternalSignature externalSignature = new X509Certificate2Signature(myCert.SCertificate, "SHA1");
MakeSignature.SignDetached(sap, externalSignature, myCert.Chain, null, null,myCert.Tsc, 0, CryptoStandard.CMS);
st.Close();
reader.Close();
}
Figure A (original)
Figure B (after digital signature applied)
Link for Pdf files
We are sucessfully signing PDF documents, using the C# sample C2_01_SignHelloWorld:
http://sourceforge.net/p/itextsharp/code/HEAD/tree/tutorial/signatures/chapter2/C2_01_SignHelloWorld/C2_01_SignHelloWorld.cs#l21
For a just about every PDF we sign, this works fine. However, as soon as there is one or more attachments, MakeSignature.SignDetached throws a NullReference exception.
Does someone hava an idea why this is happening?
Thanks!
Here is our code snippet:
PdfReader reader = new PdfReader(pdfFileName);
PdfSignatureAppearance signatureAppearance;
String pdfFileNameDest = pdfFileName.Substring(0, pdfFileName.LastIndexOf('.')) + "-signed" + pdfFileName.Substring(pdfFileName.LastIndexOf('.'));
FileStream fileStream = new FileStream(certFileName, FileMode.Open);
FileStream fileStreamDest = new FileStream(pdfFileNameDest, FileMode.Create);
Pkcs12Store store = new Pkcs12Store(fileStream, certPassword.ToCharArray());
String alias = "";
ICollection<X509Certificate> chain = new List<X509Certificate>();
try
{
// Search private key
foreach (string al in store.Aliases)
if (store.IsKeyEntry(al) && store.GetKey(al).Key.IsPrivate)
{
alias = al;
break;
}
AsymmetricKeyEntry pk = store.GetKey(alias);
foreach (X509CertificateEntry c in store.GetCertificateChain(alias))
chain.Add(c.Certificate);
RsaPrivateCrtKeyParameters parameters = pk.Key as RsaPrivateCrtKeyParameters;
PdfStamper stamper = PdfStamper.CreateSignature(reader, fileStreamDest, '\0');
signatureAppearance = stamper.SignatureAppearance;
signatureAppearance.Reason = "Because we need to sign it";
signatureAppearance.Contact = "We";
signatureAppearance.Location = "Server";
IExternalSignature pks = new PrivateKeySignature(parameters, DigestAlgorithms.SHA256);
MakeSignature.SignDetached(signatureAppearance, pks, chain, null, null, null, 0, CryptoStandard.CMS);
signatureAppearance.SetVisibleSignature(new iTextSharp.text.Rectangle(100, 100, 250, 150), 1, null);
stamper.Close();
}
catch (Exception ex)
{
MessageBox.Show("Exception: " + ex.ToString());
}
I have been trying to put together an in-memory public-key encryption infrastructure using OpenPGP via Bouncy Castle. One of our vendors uses OpenPGP public key encryption to encrypt all their feeds, and requires us to do the same, so I'm stuck with the technology and the implementation. So now I'm coding an OpenPGP encryption/ decryption toolkit for automating these feeds.
The examples at bouncycastle.org inexplicably default to writing encrypted data to and collecting keys from a file system; this is not what I want to do, so I've been trying to get everything stream-based.
I have gotten to the point where I can actually get my code to compile and run, but my encrypted payload is empty. I think I'm missing something silly, but after several days of trying this and that, I have lost the ability to objectively examine this.
My utility class contains these methods:
public static PgpPublicKey ImportPublicKey(
this Stream publicIn)
{
var pubRings =
new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(publicIn)).GetKeyRings().OfType<PgpPublicKeyRing>();
var pubKeys = pubRings.SelectMany(x => x.GetPublicKeys().OfType<PgpPublicKey>());
var pubKey = pubKeys.FirstOrDefault();
return pubKey;
}
public static Stream Streamify(this string theString, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
var stream = new MemoryStream(encoding.GetBytes(theString));
return stream;
}
public static string Stringify(this Stream theStream,
Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
using (var reader = new StreamReader(theStream, encoding))
{
return reader.ReadToEnd();
}
}
public static byte[] ReadFully(this Stream stream)
{
if (!stream.CanRead) throw new ArgumentException("This is not a readable stream.");
var buffer = new byte[32768];
using (var ms = new MemoryStream())
{
while (true)
{
var read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
public static void PgpEncrypt(
this Stream toEncrypt,
Stream outStream,
PgpPublicKey encryptionKey,
bool armor = true,
bool verify = true,
CompressionAlgorithmTag compressionAlgorithm = CompressionAlgorithmTag.Zip)
{
if (armor) outStream = new ArmoredOutputStream(outStream);
var compressor = new PgpCompressedDataGenerator(CompressionAlgorithmTag.Zip);
outStream = compressor.Open(outStream);
var data = toEncrypt.ReadFully();
var encryptor = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, verify, new SecureRandom());
encryptor.AddMethod(encryptionKey);
outStream = encryptor.Open(outStream, data.Length);
outStream.Write(data, 0, data.Length);
}
My test method looks like this:
private static void EncryptMessage()
{
var pubKey = #"<public key text>";
var clearText = "This is an encrypted message. There are many like it but this one is cryptic.";
using (var stream = pubKey.Streamify())
{
var key = stream.ImportPublicKey();
using (var clearStream = clearText.Streamify())
using (var cryptoStream = new MemoryStream())
{
clearStream.PgpEncrypt(cryptoStream,key);
cryptoStream.Position = 0;
Console.WriteLine(cryptoStream.Stringify());
Console.WriteLine("Press any key to continue.");
}
}
Console.ReadKey();
}
The result I get looks like this:
-----BEGIN PGP MESSAGE-----
Version: BCPG C# v1.7.4114.6378
Press any key to continue.
Can someone tell me what I am doing wrong?
OK, I managed to get this working. There were several problems with this implementation. One problem was that certain things had to be done in order. Here is what seems to need to happen:
The raw data needs to be put into a PgpLiteralData object
The literal data needs to be encrypted.
The encrypted data needs to be compressed.
The compressed data (optionally) needs to be armored.
The underlying streams need to be closed in order of usage.
There should be a more elegant way to do this, but the streams used by the BouncyCastle library are all frustratingly one-way, and at several points, I needed to convert the stream to a byte array to get another part to work. I include the code I used and independently verified; if someone has a verifyably better way of doing this, I would be quite interested.
public static class OpenPgpUtility
{
public static void ExportKeyPair(
Stream secretOut,
Stream publicOut,
AsymmetricKeyParameter publicKey,
AsymmetricKeyParameter privateKey,
string identity,
char[] passPhrase,
bool armor)
{
if (armor)
{
secretOut = new ArmoredOutputStream(secretOut);
}
var secretKey = new PgpSecretKey(
PgpSignature.DefaultCertification,
PublicKeyAlgorithmTag.RsaGeneral,
publicKey,
privateKey,
DateTime.UtcNow,
identity,
SymmetricKeyAlgorithmTag.Cast5,
passPhrase,
null,
null,
new SecureRandom()
);
secretKey.Encode(secretOut);
if (armor)
{
secretOut.Close();
publicOut = new ArmoredOutputStream(publicOut);
}
var key = secretKey.PublicKey;
key.Encode(publicOut);
if (armor)
{
publicOut.Close();
}
}
public static PgpPublicKey ImportPublicKey(
this Stream publicIn)
{
var pubRings =
new PgpPublicKeyRingBundle(PgpUtilities.GetDecoderStream(publicIn)).GetKeyRings().OfType<PgpPublicKeyRing>();
var pubKeys = pubRings.SelectMany(x => x.GetPublicKeys().OfType<PgpPublicKey>());
var pubKey = pubKeys.FirstOrDefault();
return pubKey;
}
public static PgpSecretKey ImportSecretKey(
this Stream secretIn)
{
var secRings =
new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(secretIn)).GetKeyRings().OfType<PgpSecretKeyRing>();
var secKeys = secRings.SelectMany(x => x.GetSecretKeys().OfType<PgpSecretKey>());
var secKey = secKeys.FirstOrDefault();
return secKey;
}
public static Stream Streamify(this string theString, Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
var stream = new MemoryStream(encoding.GetBytes(theString));
return stream;
}
public static string Stringify(this Stream theStream,
Encoding encoding = null)
{
encoding = encoding ?? Encoding.UTF8;
using (var reader = new StreamReader(theStream, encoding))
{
return reader.ReadToEnd();
}
}
public static byte[] ReadFully(this Stream stream, int position = 0)
{
if (!stream.CanRead) throw new ArgumentException("This is not a readable stream.");
if (stream.CanSeek) stream.Position = 0;
var buffer = new byte[32768];
using (var ms = new MemoryStream())
{
while (true)
{
var read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
public static void PgpEncrypt(
this Stream toEncrypt,
Stream outStream,
PgpPublicKey encryptionKey,
bool armor = true,
bool verify = false,
CompressionAlgorithmTag compressionAlgorithm = CompressionAlgorithmTag.Zip)
{
var encryptor = new PgpEncryptedDataGenerator(SymmetricKeyAlgorithmTag.Cast5, verify, new SecureRandom());
var literalizer = new PgpLiteralDataGenerator();
var compressor = new PgpCompressedDataGenerator(compressionAlgorithm);
encryptor.AddMethod(encryptionKey);
//it would be nice if these streams were read/write, and supported seeking. Since they are not,
//we need to shunt the data to a read/write stream so that we can control the flow of data as
//we go.
using (var stream = new MemoryStream()) // this is the read/write stream
using (var armoredStream = armor ? new ArmoredOutputStream(stream) : stream as Stream)
using (var compressedStream = compressor.Open(armoredStream))
{
//data is encrypted first, then compressed, but because of the one-way nature of these streams,
//other "interim" streams are required. The raw data is encapsulated in a "Literal" PGP object.
var rawData = toEncrypt.ReadFully();
var buffer = new byte[1024];
using (var literalOut = new MemoryStream())
using (var literalStream = literalizer.Open(literalOut, 'b', "STREAM", DateTime.UtcNow, buffer))
{
literalStream.Write(rawData, 0, rawData.Length);
literalStream.Close();
var literalData = literalOut.ReadFully();
//The literal data object is then encrypted, which flows into the compressing stream and
//(optionally) into the ASCII armoring stream.
using (var encryptedStream = encryptor.Open(compressedStream, literalData.Length))
{
encryptedStream.Write(literalData, 0, literalData.Length);
encryptedStream.Close();
compressedStream.Close();
armoredStream.Close();
//the stream processes are now complete, and our read/write stream is now populated with
//encrypted data. Convert the stream to a byte array and write to the out stream.
stream.Position = 0;
var data = stream.ReadFully();
outStream.Write(data, 0, data.Length);
}
}
}
}
}
My test method looked like this:
private static void EncryptMessage()
{
var pubKey = #"<public key text here>";
var clearText = #"<message text here>";
using (var stream = pubKey.Streamify())
{
var key = stream.ImportPublicKey();
using (var clearStream = clearText.Streamify())
using (var cryptoStream = new MemoryStream())
{
clearStream.PgpEncrypt(cryptoStream, key);
cryptoStream.Position = 0;
var cryptoString = cryptoStream.Stringify();
Console.WriteLine(cryptoString);
Console.WriteLine("Press any key to continue.");
}
}
Console.ReadKey();
}
Since someone asked, my decryption algorithm looked like this:
public static Stream PgpDecrypt(
this Stream encryptedData,
string armoredPrivateKey,
string privateKeyPassword,
Encoding armorEncoding = null)
{
armorEncoding = armorEncoding ?? Encoding.UTF8;
var stream = PgpUtilities.GetDecoderStream(encryptedData);
var layeredStreams = new List<Stream> { stream }; //this is to clean up/ dispose of any layered streams.
var dataObjectFactory = new PgpObjectFactory(stream);
var dataObject = dataObjectFactory.NextPgpObject();
Dictionary<long, PgpSecretKey> secretKeys;
using (var privateKeyStream = armoredPrivateKey.Streamify(armorEncoding))
{
var secRings =
new PgpSecretKeyRingBundle(PgpUtilities.GetDecoderStream(privateKeyStream)).GetKeyRings()
.OfType<PgpSecretKeyRing>();
var pgpSecretKeyRings = secRings as PgpSecretKeyRing[] ?? secRings.ToArray();
if (!pgpSecretKeyRings.Any()) throw new ArgumentException("No secret keys found.");
secretKeys = pgpSecretKeyRings.SelectMany(x => x.GetSecretKeys().OfType<PgpSecretKey>())
.ToDictionary(key => key.KeyId, value => value);
}
while (!(dataObject is PgpLiteralData) && dataObject != null)
{
try
{
var compressedData = dataObject as PgpCompressedData;
var listedData = dataObject as PgpEncryptedDataList;
//strip away the compression stream
if (compressedData != null)
{
stream = compressedData.GetDataStream();
layeredStreams.Add(stream);
dataObjectFactory = new PgpObjectFactory(stream);
}
//strip the PgpEncryptedDataList
if (listedData != null)
{
var encryptedDataList = listedData.GetEncryptedDataObjects()
.OfType<PgpPublicKeyEncryptedData>().First();
var decryptionKey = secretKeys[encryptedDataList.KeyId]
.ExtractPrivateKey(privateKeyPassword.ToCharArray());
stream = encryptedDataList.GetDataStream(decryptionKey);
layeredStreams.Add(stream);
dataObjectFactory = new PgpObjectFactory(stream);
}
dataObject = dataObjectFactory.NextPgpObject();
}
catch (Exception ex)
{
//Log exception here.
throw new PgpException("Failed to strip encapsulating streams.", ex);
}
}
foreach (var layeredStream in layeredStreams)
{
layeredStream.Close();
layeredStream.Dispose();
}
if (dataObject == null) return null;
var literalData = (PgpLiteralData)dataObject;
var ms = new MemoryStream();
using (var clearData = literalData.GetInputStream())
{
Streams.PipeAll(clearData, ms);
}
ms.Position = 0;
return ms;
}
Hi My applion is MVC3 c#, I am using itextsharp to produce PDF files for pre disgned forms. In this application I have to different forms. To generate a form I use:
public ActionResult TestPDF(long learnerID = 211, long courseID = 11)
{
var caseList = _studyCaseSvc.ListStudyCases().Where(x => x.Course_ID == courseID);
try
{
MemoryStream memoryStream = new MemoryStream();
PdfConcatenate whole = new PdfConcatenate(memoryStream);
foreach (var ca in caseList)
{
byte[] part = null;
if (ca.CaseType == "CTA")
{
part = GenerateEvaluationCAT_PDF(learnerID, ca.ID);
}
else if (ca.CaseType == "CTAH")
{
part = GenerateEvaluationCATH_PDF(learnerID, ca.ID);
}
else
{
part = null;
}
if (part != null)
{
PdfReader partReader = new PdfReader(part);
whole.AddPages(partReader);
partReader.Close();
}
}
whole.Close();
byte[] byteInfo = memoryStream.ToArray();
SendPdfToBrowser(byteInfo);
}
catch (Exception ex)
{
}
return null;
}
I get this error: An item with the same key has already been added. The error happens at AddPages. So I developed this simpler test:
private void merge()
{
try
{
FileStream output = new FileStream("p3.pdf", FileMode.Create);
PdfConcatenate pdfConcatenate = new PdfConcatenate(output, true);
PdfReader r1 = new PdfReader("p2.pdf");
MemoryStream memoryStream = new MemoryStream();
PdfStamper pdfStamper = new PdfStamper(r1, memoryStream);
pdfStamper.FormFlattening = true;
pdfStamper.Close();
PdfReader r2 = new PdfReader(memoryStream.ToArray());
//pdfConcatenate.AddPages(tempReader);
pdfConcatenate.Open();
int n = r1.NumberOfPages;
for (int i = 1; i <= n; i++)
{
PdfImportedPage imp = pdfConcatenate.Writer.GetImportedPage(r1, i);
pdfConcatenate.Writer.AddPage(imp);
}
pdfConcatenate.Writer.FreeReader(r1);
pdfStamper.Close();
r1.Close();
pdfConcatenate.Close();
}
catch (Exception ex)
{
}
}
Same error.
Well, the problem is the misconception that you can combine multiple PDF files into one by simply concatenating them. That is wrong for PDFs (just like it is wrong for most binary file formats).
Thus, you should update your GenerateAllEvaluation_PDF method to have a PdfConcatenate instance instead of your byte Array whole, cf. http://api.itextpdf.com/itext/com/itextpdf/text/pdf/PdfConcatenate.html, open each byte array returned by your GenerateEvaluationCATH_PDF method in a PdfReader, add all pages of these readers to the PdfConcatenate and eventually return the bytes generated by that class.
EDIT (I'm more into Java than C#, thus forgive minor errors)
PdfConcatenate whole = new PdfConcatenate(...);
foreach (var ca in caseList)
{
byte[] part = null;
if (ca.CaseType == "CTA")
{
part = GenerateEvaluationCAT_PDF(learnerID, ca.ID);
}
else if (ca.CaseType == "CTAH")
{
part = GenerateEvaluationCATH_PDF(learnerID, ca.ID);
}
else
{
part = ???;
}
PdfReader partReader = new PdfReader(part);
whole.AddPages(partReader);
partReader.Close();
}
The PdfConcatenate can be constructed with a MemoryStream from which you can retrieve the final byte[].
PS: PdfConcatenate may not be a part of iTextSharp version 4.x yet but it is merely a convenience wrapper of PdfCopy and PdfSmartCopy. Thus, you may simply have a look at the sources of iTextSharp (OSS after all) and be inspired: PdfConcatenate.cs.
I am working on C# program to upload image file to netsuite. Can anybody help me how to invoke netsuite script(written in
java script) in C# because I can find upload api only in netsuite script. Is there any webservices or functions
in netsuite to upload image file in netsuite ?
You can upload a file directly with SuiteTalk. Examples below are written in C#.
Call the below methods like this:
uploadFile(#"SERIAL_NUMBERS.csv", "csv", "123456");
Methods:
public static void UploadFile(string filename, string filetype, string folderId)
{
var sFileName = filename;
var sNsFileName = filename;
var sFileType = filetype;
var sFolderId = folderId;
var uploadFile = new com.netsuite.webservices.File { attachFromSpecified = true, attachFrom = FileAttachFrom._computer };
if (sFolderId != null)
{
var folderRef = new RecordRef { internalId = sFolderId };
uploadFile.folder = folderRef;
}
// Specify the NetSuite filename
if (sNsFileName != null)
uploadFile.name = sNsFileName;
uploadFile.fileTypeSpecified = true;
if (sFileType != null)
{
if (sFileType.Trim().ToLower().Equals("plaintext"))
uploadFile.fileType = MediaType._PLAINTEXT;
else if (sFileType.Trim().ToLower().Equals("image"))
uploadFile.fileType = MediaType._IMAGE;
else if (sFileType.Trim().ToLower().Equals("csv"))
uploadFile.fileType = MediaType._CSV;
else
uploadFile.fileType = MediaType._PLAINTEXT;
}
else
uploadFile.fileType = MediaType._PLAINTEXT;
uploadFile.content = LoadFile(sFileName);
// Invoke add() operation to upload the file to NetSuite
var response = Service.add(uploadFile);
// Process the response
if (response.status.isSuccess)
{
Console.WriteLine(
"\nThe file was uploaded successfully:" +
"\nFile Record key=" + ((RecordRef)response.baseRef).internalId +
"\nRenaming file");
}
else
{
Console.WriteLine("The file was not uploaded. Please notify the NetSuite team of the following error:");
DisplayError(response.status.statusDetail);
}
}
private static byte[] LoadFile(String sFileName)
{
byte[] data;
try
{
FileStream inFile;
using (inFile = new FileStream(sFileName, FileMode.Open, FileAccess.Read))
{
data = new Byte[inFile.Length];
inFile.Read(data, 0, (int)inFile.Length);
}
}
catch (Exception ex)
{
// Error creating stream or reading from it.
Console.WriteLine(ex.Message);
return null;
}
return data;
}