I am trying to print an RDL report with terms and conditions PDF. The problem is that the report itself is a Queue of images, whereas the T&C's are in PDF format. So whenever I do an "Enqueue", adding to the streams, it's looking at that PDF like one big image, as opposed to two pages. This causes a GDI+ generics error. Is there anyway for me to convert the PDF into the proper image format so that I can combine these documents? Here's the code I have so far:
internal static void DoPrintInvoice(int orderID, SalesOrderBLL.DocumentType doctype, string printer, int copies, List<string> lines)
{
using (var context = rempscoDataContext.CreateReadOnlyContext())
using (MiniProfiler.Current.Step("DoPrintInvoice()"))
{
//Customer Opt-Out
// Generate Report
using (var report = GetSalesOrderReport(orderID, _DocumentTypeDescriptions[doctype], doctype != DocumentType.InvoiceLetterhead, lines))
{
// returns queue of streams.
var streams = PrintingBLL.RenderStreams(report, landscape: false);
// returns byte array
var TermsAndConditions = GetTermsAndConditions();
//convert byte array to memory stream.
var TCStream = new MemoryStream(TermsAndConditions);
//conditional to add T&C's to stream.
if (doctype == DocumentType.OrderAcknowledgement)
{
streams.Enqueue(TCStream);
}
ParallelInvoke(
() => SaveSalesOrderPDF(orderID, doctype, report),
() => PrintingBLL.PrintStreams(streams, string.Format("Sales Order ({0})", report.DisplayName), printer, copies, false)
);
}
}
}
I've tried to convert the terms and conditions into an image, and back to a byte array but it gives me the same GDI generic issue. Any help would be greatly appreciated!
Have you looked at PDFSharp? I have had good luck working with it in the past for rendering PDFs.
www.pdfsharp.com
Related
I am trying to execute the sample program of PDFSharp library. I have already provided the references in the project.
PROBLEM: The code executes without any error and I see that the method ExportJpegImage() is called 7 times (which is the number of images in the pdf). But when I try to open the images written by the program ( in .jpeg), the Windows cannot open them.
static void Main(string[] args)
{
Console.WriteLine("Starting PDF Sharp sample program...");
const string filename = #"D:/Test/test.pdf";
PdfDocument document = PdfReader.Open(filename);
int imageCount = 0;
// Iterate pages
foreach (PdfPage page in document.Pages)
{
// Get resources dictionary
PdfDictionary resources = page.Elements.GetDictionary("/Resources");
if (resources != null)
{
// Get external objects dictionary
PdfDictionary xObjects = resources.Elements.GetDictionary("/XObject");
if (xObjects != null)
{
ICollection<pdfitem> items = xObjects.Elements.Values;
// Iterate references to external objects
foreach (PdfItem item in items)
{
PdfReference reference = item as PdfReference;
if (reference != null)
{
PdfDictionary xObject = reference.Value as PdfDictionary;
// Is external object an image?
if (xObject != null && xObject.Elements.GetString("/Subtype") == "/Image")
{
ExportJpegImage(xObject, ref imageCount);
}
}
}
}
}
}
}
static void ExportJpegImage(PdfDictionary image, ref int count)
{
// Fortunately JPEG has native support in PDF and exporting an image is just writing the stream to a file.
byte[] stream = image.Stream.Value;
FileStream fs = new FileStream(String.Format(#"D:\Test\Image{0}.jpeg", count++), FileMode.Create, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(stream);
bw.Close();
}
Please read the note preceding that example:
Note: This snippet shows how to export JPEG images from a PDF file. PDFsharp cannot convert PDF pages to JPEG files. This sample does not handle non-JPEG images. It does not (yet) handle JPEG images that have been flate-encoded.
There are several different formats for non-JPEG images in PDF. Those are not supported by this simple sample and require several hours of coding, but this is left as an exercise to the reader.
Thus, the images you want to extract from your PDF most likely simply are not embedded as JPEG images (at least not without further filtering like deflation). To transform that data into something a normal image viewer can handle, you most likely have to invest several hours of coding.
I am trying to write a method that takes image data in base64 string and saves it into a binary file while preserving transparency (for example in case of PNGs).
My other requirement is that this needs to be done in C# in PCL (Portable Class Library).
I know that you can use Image or WriteableBitmap to solve this issue but such classes are not available in PCL.
I have the following method that does the job of taking the base64 data and saving it to a file:
public static async Task Base64ToBinaryImageFile(IFile file, string base64Content)
{
var bytes = Convert.FromBase64String(base64Content);
using (var stream = await file.OpenAsync(FileAccess.ReadAndWrite))
{
stream.Seek(0, SeekOrigin.Begin);
using (var writer = new BinaryWriter(stream))
{
writer.Write(content);
writer.Flush();
}
}
}
It works fine except that:
I lose the transparency data (so transparent pixels show up as black).
The file that is created using this method has a larger size (in bytes) than the original file.
Any idea on what's the cause and how to fix these issues?
Update: Here is the JavaScript code that sends the base64 data to C#:
function onPaste(event) {
var $event = event.data.$;
var clipboardData = $event.clipboardData;
var found = false;
var imageType = /^image/;
if (!clipboardData) {
return false;
}
return Array.prototype.forEach.call(clipboardData.types, function (type, i) {
if (found) {
return false;
}
if (type.match(imageType) || clipboardData.items[i].type.match(imageType)) {
readImageAsBase64(clipboardData.items[i]);
return found = true;
}
return false;
});
}
function readImageAsBase64(item) {
if (!item || typeof item.getAsFile !== "function") {
return;
}
var file = item.getAsFile();
var reader = new FileReader();
reader.onload = function (evt) {
window.external.notify("pasteImageBase64/" + evt.target.result);
};
reader.readAsDataURL(file);
}
I foresee a few possible issues:
Your issue could reside in the base64Content provided by the caller. It's possible that the conversion to base64Content that is provided as input to your method is reading the PNG image with an incorrect file format.
Related to #1, it's possible that someone calling the method took a .JPG or .BMP image file, naively renamed it to .PNG extension and called your method assuming that they were sending a PNG image, when in fact they were not.
You may be opening the .PNG image in testing with an image viewer/editor that does not support transparency or handle it well (IE mspaint.exe)
I am trying to manipulate a PDF, it functions as a template. What I am trying is replacing 'placeholders' in the PDF template with my data. So someone makes a PDF template in Scribus for example, and adds an empty image with the name "company_logo". My application sees an image placeholder with the name "company_logo" and it adds the company logo there.
I can browse AcroFields with iTextSharp library and set text in a text field (for example) but AcroFields doesn't list the image placeholder. I've got the feeling that AcroFields is not what I am looking for.
So how can I get a list (or tree) of all objects from the PDF and read their properties (like position, size, contents, etc).
P.S. I do not necessarily need to use iTextSharp, any other PDF lib will do as well. Preferably free.
A little pseudo code to make myself more clear
var object = Pdf.GetObjectById("company_logo");
object.SetValue(myImage);
object.SetPosition(x, y);
From your pseudo-code example, we understand that you want to replace the stream of an object that contains an image. There are several examples on how to do this.
For instance, in the SpecialID example, we create a PDF where we mark a specific image with a special ID. In the ResizeImage example, we track that image based on that special ID and we replace the stream:
object = reader.getPdfObject(i);
if (object == null || !object.isStream())
continue;
stream = (PRStream)object;
if (value.equals(stream.get(key))) {
PdfImageObject image = new PdfImageObject(stream);
BufferedImage bi = image.getBufferedImage();
if (bi == null) continue;
int width = (int)(bi.getWidth() * FACTOR);
int height = (int)(bi.getHeight() * FACTOR);
BufferedImage img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
AffineTransform at = AffineTransform.getScaleInstance(FACTOR, FACTOR);
Graphics2D g = img.createGraphics();
g.drawRenderedImage(bi, at);
ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write(img, "JPG", imgBytes);
stream.clear();
stream.setData(imgBytes.toByteArray(), false, PRStream.NO_COMPRESSION);
stream.put(PdfName.TYPE, PdfName.XOBJECT);
stream.put(PdfName.SUBTYPE, PdfName.IMAGE);
stream.put(key, value);
stream.put(PdfName.FILTER, PdfName.DCTDECODE);
stream.put(PdfName.WIDTH, new PdfNumber(width));
stream.put(PdfName.HEIGHT, new PdfNumber(height));
stream.put(PdfName.BITSPERCOMPONENT, new PdfNumber(8));
stream.put(PdfName.COLORSPACE, PdfName.DEVICERGB);
}
You will find another example in the book The Best iText Questions on StackOverflow where I answered the following question: PDF Convert to Black And White PNGs
I wrote the ReplaceImage example to show how to replace the image:
public static void replaceStream(PRStream orig, PdfStream stream) throws IOException {
orig.clear();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
stream.writeContent(baos);
orig.setData(baos.toByteArray(), false);
for (PdfName name : stream.getKeys()) {
orig.put(name, stream.get(name));
}
}
As you can see, it isn't as trivial as saying:
var object = Pdf.GetObjectById("company_logo");
object.SetValue(myImage);
As I explained in my comment, this doesn't make sense:
object.SetPosition(x, y);
The objects we're manipulating are streams that are used as Image XObjects. The advantage of having Image XObjects is that they can be reused. For instance: if you have the same logo on every page, then you want to store the bytes of that image only once and reuse the same logo multiple times. This means that the object with the image bytes doesn't know anything about its position. The position is determined in the content stream. It depends on the CTM.
Did you have a look at the scribus scripting capabilities?
Since you create a tamplate in scribus You could also write a short python script which replaces your placeholders with your final data and exports the final PDF.
Since scribus 1.5 it is also possible to call the python scripts from the commandline.
I can't seem to find any documentation of how to delete a tiff tag using the LibTiff.Net library. I love the library but this one method is important to what I need to do. At one point I was hoping that I could just set a tag and set it's value to nothing. I had hoped that would work but that was a negative.
Anyone know how to delete a tiff tag using the LibTiff.Net library?
Please have a look at TiffCP utility (and especially its source code) shipped with LibTiff.Net.
LibTiff.Net doesn't offer methods for removing tags (the same is true for LibTiff). You will need to implement part of TiffCP functionality in order to achieve that.
Basically, you will need to copy all tags you wish to retain, and copy pixels data without decoding and re-encoding it.
Please also have a look at Convert a multi-strip TIFF image to a single-strip one sample. It shows how to copy tags and copy raw (undecoded data) from one image to another. The sample actually decodes data in some cases because it needs to change number of strips, but you won't need to decode data.
I think you will have to basically copy the input file to a new TIFF image filtering out the tags you don't want in the process. Take a look at the tiffcp utility which is part of the regular libtiff distribution. It sort of does that, minus filtering.
Disclaimer: I've never used LibTiff.Net and am assuming that it is very similar to the LibTiff.
Take a look at tiffcp.c
First it manually copies/sets some known tags such as resolution, compression, colors etc.
Then it copies all a set of tags that can be copied w/o preprocessing:
for (p = tags; p < &tags[NTAGS]; p++)
CopyTag(p->tag, p->count, p->type);
Then it copies the actual pixel data. This will from what I recollect, drop any tags that are not known to tiffcp. If your tag that you want to drop is in the list, you can trivially drop it by removing it from that list.
Note: As first this might look like a big answer but I wanted to make sure that whoever was looking at this see's all the "proprietary classes I have created to keep everything boxed up for cleanness. In the interest of keeping the answer as sort as possible and to be informative I will only paste the code for the DeleteTiffTags method. The rest of the code can be downloaded via here.
Now on to the good stuff... I ended up spending about a day on making this happen and it was possible thanks to various questions being answered by the great stackoverflow community. I wrote two little (very detailed) method in one of my classes to delete a tiff tag. The first one is meant to delete a list of given tags and the second one is to delete a single tag which works off the for mentioned method. Also in this example I added a few lines to support my custom tiff tags... They will all be preceded with the //ADDED comment.
Classes:
public static class TIFFTAGS
- This class is the main class that is simply called by doing something like TIFFTAGS.DeleteTiffTags(); Since it's a static class
there is no need to create an object of it to use it's methods.
private class TIFFTAGS_PAGE
- This class is a private class that resides inside the TIFFTAGS class. It's purpose is to contain all the single page info for all
the pages that might be in the tiff. It is private and only used for
internal purposes.
public class TIFFTAGS_TAG
- This is a class I made to wrap up the tags in something more to my liking. Using the standard tag type names such as ASCII, SHORT, LONG,
and RATIONAL.
Methods/Functions:
TagExtender()
- This little gem is a callback function that allows you to actually keep your CUSTOM tags in the tiff. Without it ALL of your custom tags
would disappear when you deleted any tag (even if you deleted just
one).
DeleteTiffTags()
- This is the main method that deletes a list of tags. Simply pass in a list of ushort tag numbers and all will be deleted. Keep in mind
not using the TagExtender function will cause your custom tags to go
poof!
DeleteTiffTag()
- This is simply used to delete a single tiff tag. It calls upon DeleteTiffTags() to handle the grunt work.
public static bool DeleteTiffTags(string sFileName, List<ushort> ushortTagNumbers)
{
//Deletes a list of tiff tag from the given image
//Returns true if successful or false if error occured
//Define variables
List<TIFFTAGS_PAGE> ttPage = new List<TIFFTAGS_PAGE>();
//Check for empty list
if (ushortTagNumbers.Count == 0) return false;
try
{
//ADDED
m_lTagsToWrite = new List<TIFFTAGS_TAG>();
m_lTagsToWrite.Add(new TIFFTAGS_TAG("", 38001, Convert.ToString("")));
m_lTagsToWrite.Add(new TIFFTAGS_TAG("", 38002, Convert.ToString("")));
m_parentExtender = Tiff.SetTagExtender(TagExtender);
//Open the file for reading
using (Tiff input = Tiff.Open(sFileName, "r"))
{
if (input == null) return false;
//Get page count
int numberOfDirectories = input.NumberOfDirectories();
//Go through all the pages
for (short i = 0; i < numberOfDirectories; ++i)
{
//Set the page
input.SetDirectory(i);
//Create a new tags dictionary to store all my tags
Dictionary<ushort, FieldValue[]> dTags = new Dictionary<ushort, FieldValue[]>();
//Get all the tags for the page
for (ushort t = ushort.MinValue; t < ushort.MaxValue; ++t)
{
TiffTag tag = (TiffTag)t;
FieldValue[] tagValue = input.GetField(tag);
if (tagValue != null)
{
dTags.Add(t, tagValue);
}
}
//Check if the page is encoded
bool encoded = false;
FieldValue[] compressionTagValue = input.GetField(TiffTag.COMPRESSION);
if (compressionTagValue != null)
encoded = (compressionTagValue[0].ToInt() != (int)Compression.NONE);
//Create a new byte array to store all my image data
int numberOfStrips = input.NumberOfStrips();
byte[] byteImageData = new byte[numberOfStrips * input.StripSize()];
int offset = 0;
//Get all the image data for the page
for (int n = 0; n < numberOfStrips; ++n)
{
int bytesRead;
if (encoded)
bytesRead = input.ReadEncodedStrip(n, byteImageData, offset, byteImageData.Length - offset);
else
bytesRead = input.ReadRawStrip(n, byteImageData, offset, byteImageData.Length - offset);
//Add to the offset keeping up with where we are
offset += bytesRead;
}
//Save all the tags, image data, and height, etc for the page
TIFFTAGS_PAGE tiffPage = new TIFFTAGS_PAGE();
tiffPage.Height = input.GetField(TiffTag.IMAGELENGTH)[0].ToInt();
tiffPage.Tags = dTags;
tiffPage.PageData = byteImageData;
tiffPage.Encoded = encoded;
tiffPage.StripSize = input.StripSize();
tiffPage.StripOffset = input.GetField(TiffTag.STRIPOFFSETS)[0].ToIntArray()[0];
ttPage.Add(tiffPage);
}
}
//Open the file for writing
using (Tiff output = Tiff.Open(sFileName + "-new.tif", "w"))
{
if (output == null) return false;
//Go through all the pages
for (short i = 0; i < ttPage.Count(); ++i)
{
//Write all the tags for the page
foreach (KeyValuePair<ushort, FieldValue[]> tagValue in ttPage[i].Tags)
{
//Write all the tags except the one's needing to be deleted
if (!ushortTagNumbers.Contains(tagValue.Key))
{
TiffTag tag = (TiffTag)tagValue.Key;
output.GetTagMethods().SetField(output, tag, tagValue.Value);
}
}
//Set the height for the page
output.SetField(TiffTag.ROWSPERSTRIP, ttPage[i].Height);
//Set the offset for the page
output.SetField(TiffTag.STRIPOFFSETS, ttPage[i].StripOffset);
//Save page data along with tags
output.CheckpointDirectory();
//Write each strip one at a time using the same orginal strip size
int numberOfStrips = ttPage[i].PageData.Length / ttPage[i].StripSize;
int offset = 0;
for (int n = 0; n < numberOfStrips; ++n)
{
//Write all the image data (strips) for the page
if (ttPage[i].Encoded)
//output.WriteEncodedStrip(n, byteStrip, offset, byteStrip.Length - offset);
output.WriteEncodedStrip(0, ttPage[i].PageData, offset, ttPage[i].StripSize - offset);
else
output.WriteRawStrip(n, ttPage[i].PageData, offset, ttPage[i].StripSize - offset);
//Add to the offset keeping up with where we are
offset += ttPage[i].StripOffset;
}
//Save the image page
output.WriteDirectory();
}
}
//ADDED
Tiff.SetTagExtender(m_parentExtender);
}
catch
{
//ADDED
Tiff.SetTagExtender(m_parentExtender);
//Error occured
return false;
}
//Return success
return true;
}
My name is Ed and i need load image from ReportView dinamic.How i can do this?
I work windows forms,c# 3.0 and linq to sql, i need load image to my reports dinamic.
Thanks.
I'm assuming that you are using the Microsoft Report Viewer Component from C# and you want to add an image to the report dynamically.
This is certainly possible, you need to create a class with a byte[] property that represents the serialized bitmap.
class ReportImage {
public byte[] Image {get;set;}
// Other stuff here if you want...
}
Set the property of this object the a 24 bit per pixel serialized version of your Bitmap (i.e. save your bitmap to a MemoryStream, then call MemoryStream.ToArray()). You must use 24 bits per pixel, and the format you save to must be BMP, this seems to be required in the Report Viewer.
You can then bind to the Object Data Source, (see the MSDN documentation for details on binding to Objects, also see the example here). Use the Image item to display your image in the report.
The limitation is that the images in your report must be fixed size. You'll have to resample the images beforehand to fit them in, or, as Jon suggests, dynamically create the RDLC file for the report.
This answer is very helpful (it got me past having little "broken image" boxes on my report), but a little misleading.
It is, strictly speaking, NOT a requirement that the "image" (which is actually a byte array) be a BMP format. In a test project I was able to read jpeg files from disk (i.e. File.ReadAllBytes(filename); ) and add the resulting byte arrays to a byte[] property in a List of "rptrow" (where rptrow is a object that represents all the data for one row in a report table). The images on the report had the MIMEType set to "image/jpeg" and a Source property of "Database". I also noticed that it did not matter what MIMEType I used as long as something was specified, (i.e. not blank).
I was in a hurry, so I didn't even consider checking the statement that it had to be a 24bpp image.
Simplified rptobj:
public class rptobj
{
public string FileName { get; set; }
public byte[] Photo { get; set; }
private List<rptobj> photos;
public List<rptobj> GetList()
{
if (photos == null)
{
photos = LoadPhotos();
}
return photos;
}
private List<rptobj> LoadPhotos()
{
var rslt = new List<rptobj>();
byte[] rawData;
var path = HttpContext.Current.Server.MapPath(#"~\images");
DirectoryInfo di = new DirectoryInfo(path);
FileSystemInfo[] fis = di.GetFileSystemInfos("*.jpg");
foreach(var fi in fis){
rawData = File.ReadAllBytes(string.Format(#"{0}\{1}", path, fi.Name ));
rslt.Add(new rptobj() { Photo = rawData, FileName = fi.Name });
}
return rslt;
}
}
Short answer is that you cannot do this, at least not with the built in report viewer functions.
However, if you are sure you want to do this, you can attempt to dynamically create RDLC files. If you create your RDLC files dynamically, you can dynamically add images to the reports.
You can find some sample code on how to create RDLC files dynamically here.