Print preview using custom paginator and XpsDocumentWriter not displaying in DocumentViewer - c#

I have created a custom document paginator that takes a datatable and prints exactly as I need. I would like to do a print preview. I have read all the posts on how to create a xps file in memory and then display it. I just can't get it to work. Here is my code. I am using a MVVM pattern. Please note the line of code _data.DocView=fds; This passes the data to my view model.
PrintDialog dialog = new PrintDialog();
dialog.ShowDialog();
StoreDataSetPaginator paginator = new StoreDataSetPaginator(dt, new Typeface("Calibri"), 8, 96 * 0.75,
new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight));
//this is commented out to attempt the print preview
// dialog.PrintDocument(paginator, "Print out");
MemoryStream ms = new MemoryStream();
Package package = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
Uri DocumentUri = new Uri("pack://InMemoryDocument.xps");
PackageStore.AddPackage(DocumentUri, package);
XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.NotCompressed,
DocumentUri.AbsoluteUri);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
writer.Write(paginator);
IDocumentPaginatorSource fds = xpsDocument.GetFixedDocumentSequence();
_data.DocView = fds;
PrintPreviewConduit prntv = new PrintPreviewConduit();
prntv.Show();
Now here is my view model:
private IDocumentPaginatorSource _docView;
public IDocumentPaginatorSource DocView
{
get { return _docView; }
set
{
_docView = value;
OnPropertyChanged("DocView");
}
}
And finally my XAML:
<Grid>
<DocumentViewer Name="docview" Document="{Binding DocView}"/>
</Grid>
I entered a break point in my ViewModel at "public IDocumentPaginatorSource DocView" and when I roll my mouse over it I get "System.Windows.Documents.FixedDocumentSequence. Not sure what i should be getting. I spent a good while now and any help would be greatly appreciated.
Sys

Well I feel stupid. I did not set the new window's datacontext to my view model. Now everything works!!!!
PrintPreviewConduit prntv = new PrintPreviewConduit();
prntv.DataContext = _data;
_data.DocView = fds;
prntv.Show();

Related

Footer not showing in iTextSharp v4

I'm trying to create a PDF from HTML, and am using iTextSharp for that. From my reading, the license that covers the newer versions of iTextSharp would require me to make the source code available. We can't do that, so we're using version 4, which is under the LGPL.
I'm trying to get a footer to appear along with the HTML, but it's not working for some reason. I've tried removing the HTML and just using text. Just putting a chunk in the footer. Multiple pages vs single pages. Hopefully I'm just missing something easy, but from the examples I've seen it should be super easy.
using (MemoryStream ms = new MemoryStream())
{
Document doc = new Document(PageSize.LETTER, 35,35,35,70);
var font = FontFactory.GetFont("arial", 8f);
font.Color = Color.BLACK;
var chunk = new Chunk("Footer", font);
var phrase = new Phrase(chunk);
var footer = new HeaderFooter(phrase, true);
footer.Alignment = 1;
footer.Border = Rectangle.NO_BORDER;
doc.Footer = footer;
//doc.Footer = new HeaderFooter(new Phrase("Footer"),false);
var writer = PdfWriter.GetInstance(doc, ms);
var htmlWorker = new HTMLWorker(doc);
using (var sr = new StringReader(html))
{
doc.Open();
doc.Add(new Chunk("Text"));
//htmlWorker.Parse(sr);
doc.Close();
}
return ms.ToArray();
}
Watch your HeaderFooter ctor. The signature your are using might lead to setting the header text only.
Anyway maybe use the PdfWriter.PageEvent and some class deriving from PdfPageEventHelper to implement header and footer (instead of HeaderFooter())

How to add in reply to annotation using iTextSharp

I am trying to add a sticky note reply to in pdf using iTextSharp. I am able to create a new annotation in the pdf. But i cannot link it as child of an already existing annotation. I copied most of the properties in parent to its child. I copied it by analyzing the properties of a reply, by manually adding a reply from Adobe Reader. What I am missing is the property /IRT. It needs a reference to the parent popup. Like /IRT 16 0 R.
Below is the code i am trying.
private void annotateReplyPdf()
{
string outputFile = #"D:\temp\temp.pdf";
// Creating iTextSharp.text.pdf.PdfReader object to read the Existing PDF Document
using (PdfReader reader = new PdfReader(FILE_NAME))
{
using (FileStream fs = new FileStream(outputFile, FileMode.Create, FileAccess.Write, FileShare.None))
{
// Creating iTextSharp.text.pdf.PdfStamper object to write Data from iTextSharp.text.pdf.PdfReader object to FileStream object
using (PdfStamper stamper = new PdfStamper(reader, fs))
{
//get page 1
PdfDictionary pageDic = reader.GetPageN(1);
//get annotations in page 1
PdfArray pageAnnotsArray = pageDic.GetAsArray(PdfName.ANNOTS);
if (pageAnnotsArray != null)
{
PdfDictionary curAnnotDic = pageAnnotsArray.GetAsDict(0);
PdfArray rect = curAnnotDic.GetAsArray(PdfName.RECT);
Rectangle rectangle = new Rectangle(float.Parse(rect[0].ToString()), float.Parse(rect[1].ToString()), float.Parse(rect[2].ToString()), float.Parse(rect[3].ToString()));
PdfAnnotation newAnnot = new PdfAnnotation(stamper.Writer, rectangle);
newAnnot.Title = "john.conor";
var dtNow = DateTime.Now;
newAnnot.Put(PdfName.C, curAnnotDic.Get(PdfName.C));
newAnnot.Put(PdfName.CONTENTS, new PdfString("Reply using prog"));
newAnnot.Put(PdfName.CREATIONDATE, new PdfDate(dtNow));
// newAnnot.Put(PdfName.IRT, curAnnotDic.); stuck here
newAnnot.Put(PdfName.M, new PdfDate(dtNow));
newAnnot.Put(PdfName.NAME, curAnnotDic.Get(PdfName.NAME));
newAnnot.Put(PdfName.RC, curAnnotDic.Get(PdfName.RC));
newAnnot.Put(PdfName.SUBTYPE, PdfName.TEXT);
newAnnot.Put(PdfName.SUBJECT, curAnnotDic.Get(PdfName.SUBJECT));
stamper.AddAnnotation(newAnnot, 1);
}
}
}
}
}
The methods I have used might not be accurate or efficient, as most of the code were found by trial and error and checking other similar examples(also checking the pdf specification).
Can somebody please fill that code, which does the magic.
note: SO question doesn't provide a code for the answer.
Please take a look at the AddInReplyTo example.
We have a file named hello_sticky_note.pdf that looks like this:
I am going to skip the method to detect the annotation of the sticky note (in your question, you already have this code). In my example, I know that this annotation is the first entry in the /Annots array (the annotation with index 0).
This is how I'm going to add an "in reply to" annotation:
public void manipulatePdf(String src, String dest) throws IOException, DocumentException {
PdfReader reader = new PdfReader(src);
PdfDictionary page = reader.getPageN(1);
PdfArray annots = page.getAsArray(PdfName.ANNOTS);
PdfDictionary sticky = annots.getAsDict(0);
PdfArray stickyRect = sticky.getAsArray(PdfName.RECT);
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
PdfWriter writer = stamper.getWriter();
Rectangle stickyRectangle = new Rectangle(
stickyRect.getAsNumber(0).floatValue(), stickyRect.getAsNumber(1).floatValue(),
stickyRect.getAsNumber(2).floatValue(), stickyRect.getAsNumber(3).floatValue()
);
PdfAnnotation replySticky = PdfAnnotation.createText(
writer, stickyRectangle, "Reply", "Hello PDF", true, "Comment");
replySticky.put(PdfName.IRT, annots.getAsIndirectObject(0));
stamper.addAnnotation(replySticky, 1);
stamper.close();
}
Just like you, I get the original annotation (in my code, it's named sticky) and I get the position of that annotation (stickyRect). I create a stickyRectangle object in a slightly different way than you do (my way is better, but that doesn't matter too much) and I use that stickyRectangle to create a new PdfAnnotation named replySticky.
That's what you already have. Now I add the missing part:
replySticky.Put(PdfName.IRT, annots.GetAsIndirectObject(0));
In your code, you add the annotation dictionary, but what you actually need is the reference to that dictionary.
The resulting PDF looks like hello_in_reply_to.pdf:

XpsDocument ignores my height settings

I am facing a problem when I try to declare a PageHeight on an XPS Document.
My code so far looks like this:
private FixedDocumentSequence GetDocument(DocumentPaginator paginator)
{
FixedDocumentSequence document = null;
string tempFileName = System.IO.Path.GetTempFileName();
File.Delete(tempFileName);
using (var xpsDocument = new XpsDocument(tempFileName, FileAccess.ReadWrite, CompressionOption.NotCompressed))
{
var writer = XpsDocument.CreateXpsDocumentWriter(xpsDocument);
var printTicket = new PrintTicket
{
PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA4, paginator.PageSize.Width, paginator.PageSize.Height),
PageBorderless = PageBorderless.Borderless,
PageMediaType = PageMediaType.None,
};
writer.Write(paginator, printTicket);
//paginator.PageSize.Height = 1122
var d = xpsDocument.GetFixedDocumentSequence();
//d.DocumentPaginator.PageSize.Height = 1056
document = d;
}
return document;
}
The Problem is that I am declaring a PageSize Height of 1122 which I am getting from this code:
var pd = new PrintDialog();
var sz = new Size(pd.PrintableAreaWidth, pd.PrintableAreaHeight);
but when I look into the property
d.DocumentPaginator.PageSize.Height
I can see that the Height is 1056, this height happens to be the Height of the NorthAmericanLetter Page format height, I am already specifying a specific printTicket with an explicit PageMediaSize what else could be wrong?
Edit:
Here is my edited code, but this give the same result:
private FixedDocumentSequence GetDocument(DocumentPaginator paginator)
{
FixedDocumentSequence document = null;
string oldTempFileName = System.IO.Path.GetTempFileName();
File.Delete(oldTempFileName);
XpsDocument oldXpsDocument;
using (oldXpsDocument = new XpsDocument(oldTempFileName, FileAccess.ReadWrite, CompressionOption.NotCompressed))
{
var writer = XpsDocument.CreateXpsDocumentWriter(oldXpsDocument);
var printTicket = new PrintTicket
{
PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA4, paginator.PageSize.Width, paginator.PageSize.Height),
PageBorderless = PageBorderless.Borderless,
PageMediaType = PageMediaType.None,
};
writer.Write(paginator, printTicket);
}
//string newTempFileName = System.IO.Path.GetTempFileName();
//File.Delete(newTempFileName);
using (var newXpsDocument = new XpsDocument(oldTempFileName, FileAccess.Read, CompressionOption.NotCompressed))
{
var d = newXpsDocument.GetFixedDocumentSequence();
document = d;
}
return document;
}
I looked into the file that got created with the first using block and I still can see that the height of the pages is 1056, output of the 1.fpage file:
<FixedPage
xmlns="http://schemas.microsoft.com/xps/2005/06" xmlns:x="http://schemas.microsoft.com/xps/2005/06/resourcedictionary-key"
xml:lang="und"
Width="816" Height="1056">
<FixedPage.Resources>
<ResourceDictionary>
<LinearGradientBrush x:Key="b0" StartPoint="33,0" EndPoint="33,23" ColorInterpolationMode="SRgbLinearInterpolation" MappingMode="Absolute" SpreadMethod="Pad">
Edit2:
Found the a solution for my problem.
In my DocumentPaginator I had to override the GetPage Method and there I return a new DocumentPage(visual), this constructor has an overload that lets me set the PageSize only if I set it there its setting the PageSizes correctly.
http://msdn.microsoft.com/en-us/library/system.windows.documents.documentpage(v=vs.110).aspx
http://msdn.microsoft.com/en-us/library/ms597306(v=vs.110).aspx
My previous code:
public override DocumentPage GetPage(int pageNumber)
{
// create page element (PageTemplate is a custom usercontrol that can hold content)
var page = new PageTemplate(this, pageNumber + 1);
// arrange the elements on the page
page.Arrange(new Rect(0, 0, PageSize.Width, PageSize.Height));
// return new document page
return new DocumentPage(page);
}
Now with the second contructor:
public override DocumentPage GetPage(int pageNumber)
{
// create page element (PageTemplate is a custom usercontrol that can hold content)
var page = new PageTemplate(this, pageNumber + 1);
// arrange the elements on the page
page.Arrange(new Rect(0, 0, PageSize.Width, PageSize.Height));
// return new document page
return new DocumentPage(page, PageSize, new Rect(0, 0, 10, 10), new Rect());
}
The problem is that you're expecting the xpsDocument to have the same dimensions of the file that you created.
When you create an XpsDocument, its going to have a default FixedDocumentSequence. Since you're creating an XpsDocument, and not opening one, you're getting the default fixed document sequence, which is why you are seeing 1056 as the height--default that is the default.
However, if you look at the code example at the bottom of FixedDocumentSequence, you'll see LoadDocumentViewer, where it replaces an old XpsDocument with a new one.
When you call writer.Write(paginator, printTicket); it will write an XpsDocument to the file you specified using the PrintTicket you provided. Now you need to create an XpsDocument using that file path, and when you open it, you can then get the FixedDocumentSequence of that document and the sizes with match the PrintTicket properties. So in pseudo-code, you should do this:
string fileName = ...
using Create XpsDocument at fileName
Create XpsDocumentWriter
Create and setup PrintTicket
Write to the file using your DocumentPaginator and PrintTicket
using Create XpsDocument reading from fileName
document = opened XpsDocument GetFixedDocumentSequence()
Edit:
Good find in your last edit. I printed an XPS document using MS Word with the A4 sized setting and all the pages come up as 1056 x 816 also. I then created an XPS document using the A3 size and that comes up as 1587.36 x 1122.56. There is clearly something going on internally that we cannot see; my guess is that since the page sizes are similar in dimension, it just uses the 1056 x 816 dimensions? Who really knows... I would suggest filing a bug report to Microsoft.
In the meantime, maybe try to change d.DocumentPaginator.PageSize or even d.PrintTicket objects and try to get the page size to change that way. If there is a way to enumerate the FixedPage's you could change their settings manually too.

Extract a single page from an XPS document

I need to split an existing XPS Document and create a new XPS Document with only one page of the original one. I tried to copy the document and delete pages from the copied document, but that's very slow. Is there a more efficient way to do this? In C# please.
Thanks.
Resolved:
public void Split(string originalDocument, string detinationDocument)
{
using (Package package = Package.Open(originalDocument, FileMode.Open, FileAccess.Read))
{
using (Package packageDest = Package.Open(detinationDocument))
{
string inMemoryPackageName = "memorystream://miXps.xps";
Uri packageUri = new Uri(inMemoryPackageName);
PackageStore.AddPackage(packageUri, package);
XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.Maximum, inMemoryPackageName);
XpsDocument xpsDocumentDest = new XpsDocument(packageDest, CompressionOption.Normal, detinationDocument);
var fixedDocumentSequence = xpsDocument.GetFixedDocumentSequence();
DocumentReference docReference = xpsDocument.GetFixedDocumentSequence().References.First();
FixedDocument doc = docReference.GetDocument(false);
var content = doc.Pages[2];
var fixedPage = content.GetPageRoot(false);
var writter = XpsDocument.CreateXpsDocumentWriter(xpsDocumentDest);
writter.Write(fixedPage);
xpsDocumentDest.Close();
xpsDocument.Close();
}
}
}
Open the XpsDocument
Create the destination XpsDocument (same method)
Get the FixedDocumentSequece from the first XpsDocument
Get the first FixedDocument from the sequence.
Get the first PageContent from the Pages property
Get the FixedPage from the Child property of the PageContent
Get the XpsDocumentWriter from the second XpsDocument
Write the FixedPage
Easy.
As noted by Christopher Currens, it may be necessary to use PageContent.GetPageRoot instead of Child in step 6.
Thank you, it can help a lot people looking for a workaround against limitation of Xps printing which ignores PrintTicket defined at page level.
https://connect.microsoft.com/VisualStudio/feedback/details/529120/printqueue-addjob-ignores-printtickets-in-xps-documents

Excel, Word, PDFs AND PowerPoint print dialogues

Anybody know how to get all of these in a C# app that leads the user to a print dialogue screen?
What I mean is this:
In the gui the user selects a document, right clicks on the doc, chooses Print. Instead of immediately printing out the doc, the print dialogue comes up.
Anybody know the syntax for all of this? I tried using all of the interops for the MS Office apps and trying the printdialogue method...but no such luck.
Can anybody provide me with some code? I've seriously been at this for HOURS and can't come up with a thing.
Maybe see this previous Q&A:
send-document-to-printer-with-c#
Or, for a WPF app that prints a FlowDocument :
private void DoThePrint(System.Windows.Documents.FlowDocument document)
{
// Clone the source document's content into a new FlowDocument.
// This is because the pagination for the printer needs to be
// done differently than the pagination for the displayed page.
// We print the copy, rather that the original FlowDocument.
System.IO.MemoryStream s = new System.IO.MemoryStream();
TextRange source = new TextRange(document.ContentStart, document.ContentEnd);
source.Save(s, DataFormats.Xaml);
FlowDocument copy = new FlowDocument();
TextRange dest = new TextRange(copy.ContentStart, copy.ContentEnd);
dest.Load(s, DataFormats.Xaml);
// Create a XpsDocumentWriter object, implicitly opening a Windows common print dialog,
// and allowing the user to select a printer.
// get information about the dimensions of the seleted printer+media.
System.Printing.PrintDocumentImageableArea ia = null;
System.Windows.Xps.XpsDocumentWriter docWriter = System.Printing.PrintQueue.CreateXpsDocumentWriter(ref ia);
if (docWriter != null && ia != null)
{
DocumentPaginator paginator = ((IDocumentPaginatorSource)copy).DocumentPaginator;
// Change the PageSize and PagePadding for the document to match the CanvasSize for the printer device.
paginator.PageSize = new Size(ia.MediaSizeWidth, ia.MediaSizeHeight);
Thickness t = new Thickness(72); // copy.PagePadding;
copy.PagePadding = new Thickness(
Math.Max(ia.OriginWidth, t.Left),
Math.Max(ia.OriginHeight, t.Top),
Math.Max(ia.MediaSizeWidth - (ia.OriginWidth + ia.ExtentWidth), t.Right),
Math.Max(ia.MediaSizeHeight - (ia.OriginHeight + ia.ExtentHeight), t.Bottom));
copy.ColumnWidth = double.PositiveInfinity;
//copy.PageWidth = 528; // allow the page to be the natural with of the output device
// Send content to the printer.
docWriter.Write(paginator);
}
}

Categories

Resources