I'm in the process of trying to print out a FlowDocument that is being viewed by the user. I wrote a routine to create a copy of the original document so that changes (from the PrintDialog) don't get reflected into the DocumentViewer.
Unfortunately, my copy seems to have lost all of the information bound to its fields. I've tried resetting the DataContext but the copy's IsLoaded property is still coming back false, leading me to believe that the binding isn't occuring.
Any ideas?
Here's the code I'm using to copy the document:
private static void AddDocument(FlowDocument from, FlowDocument to)
{
TextRange tr = new TextRange(from.ContentStart, from.ContentEnd);
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
TextRange tr2 = null;
System.Windows.Markup.XamlWriter.Save(tr, ms);
tr.Save(ms, DataFormats.XamlPackage, true);
tr2 = new TextRange(to.ContentEnd, to.ContentEnd);
tr2.Load(ms, DataFormats.XamlPackage);
}
}
And here's the code I'm using to print the document:
public static void PrintFlowDocument(FlowDocument fd, string title)
{
PrintDialog pd = new PrintDialog();
IDocumentPaginatorSource idps = null;
FlowDocument flowDoc = new FlowDocument();
AddDocument(fd, flowDoc);
flowDoc.DataContext = fd.DataContext;
flowDoc.PageHeight = pd.PrintableAreaHeight;
flowDoc.PageWidth = pd.PrintableAreaWidth;
flowDoc.PagePadding = new Thickness(50);
flowDoc.ColumnGap = 0;
flowDoc.ColumnWidth = pd.PrintableAreaWidth;
idps = flowDoc;
if (pd.ShowDialog() == true)
{
pd.PrintDocument(idps.DocumentPaginator, title);
}
}
Thanks in advance,
Sonny
Notice some off about this line?
tr2 = new TextRange(to.ContentEnd, to.ContentEnd);
I had similar issues and found the forcing the document creation to a background thread gave the binding an opportunity to fire. Otherwise, it would not happen on the UI thread.
So, if your copy document method were a function it would be something like this:
Dim flowDoc As FlowDocument =
DirectCast(<ViewInstance>.UIDispatcher.Invoke(Function()
AddFlowDocument(fd),
Windows.Threading.DispatcherPriority.Background),
FlowDocument)
Related
I have a problem with printing data and image in PDF. I use the code below and it print image and data separately. But I want them to be in one PDF, how can I do this?
PrintDialog export = new PrintDialog();
string data = "data";
System.Windows.Documents.FlowDocument flowDoc = new System.Windows.Documents.FlowDocument(new System.Windows.Documents.Paragraph(new System.Windows.Documents.Run(data)));
System.Windows.Documents.IDocumentPaginatorSource source = flowDoc;
var result = export.ShowDialog();
if(result == true)
{
export.PrintVisual(souradnySystem, "Visual");
export.PrintDocument(source.DocumentPaginator, "Text");
}
Add the image to the FlowDocument before proceeding to print.
Assuming "souradnySystem" is your image (System.Windows.Controls.Image), it will be like the following:
string data = "data";
FlowDocument flowDoc = new FlowDocument(new Paragraph(new Run(data)));
flowDoc.Blocks.Add(new BlockUIContainer(souradnySystem));
I'm using DocumentViewer in a WPF XAML project. I create a fixed document with the following code. The page looks fine in the viewer. When I print it, it prints in landscape view, but the top 1-2 cm is cut off! I tried it without the scale transform on the dockpanel but that had no effect.
private void PrintReport()
{
PrintDialog pd = new PrintDialog();
pd.PrintQueue = LocalPrintServer.GetDefaultPrintQueue();
pd.PrintTicket = pd.PrintQueue.DefaultPrintTicket;
pd.PrintTicket.PageOrientation = PageOrientation.Landscape;
pd.PrintDocument(docViewer.Document.DocumentPaginator, "DaySheet");
}
public void BuildReport(DockPanel dpPrint)
{
try
{
// init
ScaleTransform st = new ScaleTransform(0.5, 0.5, 0, 0);
dpPrint.RenderTransform = st;
Size pgSize = new Size(96 * 11.69, 96 * 8.27);
FixedDocument fd = new FixedDocument();
// add page content
FixedPage fp = new FixedPage();
fp.Width = pgSize.Width;
fp.Height = pgSize.Height;
fp.Children.Add(dpPrint);
// set page content
PageContent pc = new PageContent();
pc.Child = fp;
fd.Pages.Add(pc);
// set up fresh XpsDocument
var uri = new Uri("pack://daysheet_report.xps");
PackageStore.RemovePackage(uri);
var stream = new MemoryStream();
var package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);
PackageStore.AddPackage(uri, package);
var xpsDoc = new XpsDocument(package, CompressionOption.NotCompressed, uri.AbsoluteUri);
// write FixedDocument to the XpsDocument
var docWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
docWriter.Write(fd);
// display XpsDocument in DocumentViewer
docViewer.Document = xpsDoc.GetFixedDocumentSequence();
docViewer.Document.DocumentPaginator.PageSize = pgSize;
}
catch (Exception ex)
{
gFunc.ProcessError(ex);
}
}
I tried playing around with printer drivers but it seemed to make no difference. The printer driver I have installed is the correct one for my laser printer and the right version for Windows 10 64-bit. When I print from Word, Excel, PDF its all fine, but from my WPF app it crops the top part of the page content.
As is so often the case, I got around the problem with a hack. I just changed the page content to use a StackPanel and I placed a blank TextBlock as the first child to push the other content down.
I am trying to overlay two PDF files using iText7/C#.
The first one is kind of background and the second one is containing form fields.
Everything works fine and only problem is that I lose fonts from the second file.
I try as follows:
static public bool Overlay(string back_path, string front_path, string merge_path)
{
PdfReader reader;
PdfDocument pdf = null, front;
try
{
reader = new PdfReader(back_path);
pdf = new PdfDocument(reader, new PdfWriter(merge_path));
front = new PdfDocument(new PdfReader(front_path));
var form = PdfAcroForm.GetAcroForm(front, false);
PdfAcroForm dform = PdfAcroForm.GetAcroForm(pdf, true);
IDictionary<String, PdfFormField> fields = form.GetFormFields();
// copy styles
dform.SetDefaultResources(form.GetDefaultResources());
dform.SetDefaultAppearance(form.GetDefaultAppearance().GetValue());
// do overlay
foreach (KeyValuePair<string, PdfFormField> pair in fields)
{
try
{
var field = pair.Value;
PdfPage page = field.GetWidgets().First().GetPage();
int pg_no = front.GetPageNumber(page);
if (pg_no < front_start_page || pg_no > front_end_page)
continue;
PdfObject copied = field.GetPdfObject().CopyTo(pdf, true);
PdfFormField copiedField = PdfFormField.MakeFormField(copied, pdf);
// The following returns null. If it returns something, I think I could use copiedField.setFont(font).
// var font = field.GetFont();
dform.AddField(copiedField, pdf.GetPage(pg_no));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Overlaying field {pair.Key} failed. ({ex.Message})");
}
}
pdf.Close();
return true;
}
catch (Exception ex)
{
throw new OverlayException(ex.Message);
}
}
public static PdfDictionary get_font_dict(PdfDocument pdfDoc)
{
PdfDictionary acroForm = pdfDoc.GetCatalog().GetPdfObject().GetAsDictionary(PdfName.AcroForm);
if (acroForm == null)
{
return null;
}
PdfDictionary dr = acroForm.GetAsDictionary(PdfName.DR);
if (dr == null)
{
return null;
}
PdfDictionary font = dr.GetAsDictionary(PdfName.Font);
return font;
}
So basically I get all fonts from the second PDF and copy them to the final PDF.
But it does not work.
Logically, I think setting font of the original field to the copied one is the right way.
I mean PdfFormField.GetFont() and SetFont().
But it always returns null.
In a comment you clarified:
the background PDF can be assumed not to have form fields or annotations. I mean we can assume background PDF only contains static content (scanned form) and the front PDF only contains formfields.
In that case the easiest way to implement your method is to add the background as xobject to the form PDF instead of adding the form to the background PDF.
You can simply do that like this:
PdfReader formReader = new PdfReader(front_path);
PdfReader backReader = new PdfReader(back_path);
PdfWriter writer = new PdfWriter(merge_path);
using (PdfDocument source = new PdfDocument(backReader))
using (PdfDocument target = new PdfDocument(formReader, writer))
{
PdfFormXObject xobject = source.GetPage(1).CopyAsFormXObject(target);
PdfPage targetFirstPage = target.GetFirstPage();
PdfStream stream = targetFirstPage.NewContentStreamBefore();
PdfCanvas pdfCanvas = new PdfCanvas(stream, targetFirstPage.GetResources(), target);
Rectangle cropBox = targetFirstPage.GetCropBox();
pdfCanvas.AddXObject(xobject, cropBox.GetX(), cropBox.GetY());
}
Depending on the exact static contents of the background and the form PDF, you might want to use NewContentStreamAfter instead of NewContentStreamBefore or even to use some nifty blend mode to get the exact static content look you want.
I'd like to print my programmatically created flowdocument in landscape mode and I tried all versions what I've found but none of them works.
Here's my code below:
try
{
// Create a PrintDialog
PrintDialog printDlg = new PrintDialog();
printDlg.PrintTicket.PageOrientation = System.Printing.PageOrientation.Landscape;
// Create a FlowDocument dynamically.
FlowDocument doc = CreateFlowDocumentSum();
doc.Name = "FlowDoc";
doc.ColumnWidth = printDlg.PrintableAreaWidth;
// Create IDocumentPaginatorSource from FlowDocument
IDocumentPaginatorSource idpSource = doc;
// Call PrintDocument method to send document to printer
printDlg.PrintDocument(idpSource.DocumentPaginator, "sum");
doc.Blocks.Clear();
sumTable.Clear();
}
catch
{ }
I did it finally.
Just modified the code in the print button event:
PrintDialog printDlg = new PrintDialog();
LocalPrintServer ps = new LocalPrintServer();
PrintQueue pq = ps.DefaultPrintQueue;
PrintTicket pt = pq.UserPrintTicket;
pt.PageOrientation = PageOrientation.Landscape;
FlowDocument doc = CreateFlowDocumentSum();
doc.PageHeight = 768;
doc.PageWidth = 1104;
PageMediaSize pageMediaSize = new PageMediaSize(doc.PageWidth, doc.PageHeight);
pt.PageMediaSize = pageMediaSize;
IDocumentPaginatorSource source = doc as IDocumentPaginatorSource;
printDlg.PrintDocument(source.DocumentPaginator, "sum");
Then in my FlowDocument I set the width and height:
FlowDocument docSum = new FlowDocument();
docSum.PageHeight = 768;
docSum.PageWidth = 1104;
docSum.ColumnWidth = 1104;
I have been trying for long but no success i have an existing pdf that i wan to load to my current C# app and want to create a simpe pusbutton to it , plaese cite some working code the default directory of the pdf is "C:\abc.pdf".
I am using itextsharp, C# VS 2010
Thanks
The closest solution I can find is something like the following.
static void AddPushbuttonField(string inputFile, iTextSharp.text.Rectangle buttonPosition, string buttonName, string outputFile)
{
using (PdfStamper stamper = new PdfStamper(new PdfReader(inputFile), File.Create(outputFile)))
{
PushbuttonField buttonField = new PushbuttonField(stamper.Writer, buttonPosition, buttonName);
stamper.AddAnnotation(buttonField.Field, 1);
stamper.Close();
}
}
This came from here, but was not ranked up as a solution. The code looks good and based on my experience with itextsharp I think this will do the trick.
Source:
Adding button in a pdf file using iTextSharp
Rectangle _rect;
_rect = new Rectangle(50, 100, 100, 100);
PushbuttonField button = new PushbuttonField(writer, _rect, "button");
PdfAnnotation widget = button.Field;
button.BackgroundColor = new GrayColor(0.75f);
button.BorderColor = GrayColor.GRAYBLACK;
button.BorderWidth = 1;
button.BorderStyle = PdfBorderDictionary.STYLE_BEVELED;
button.TextColor = GrayColor.GRAYBLACK;
button.FontSize = 11;
button.Text = "Text";
button.Layout = PushbuttonField.LAYOUT_ICON_LEFT_LABEL_RIGHT;
button.ScaleIcon = PushbuttonField.SCALE_ICON_ALWAYS;
button.ProportionalIcon = true;
button.IconHorizontalAdjustment = 0;