WPF Visual Print Component
Introduction
Often we need to print the screen or some parts of the screen. This is pretty useful for printing graphic reports, like charts. In this article, we’re going to have a look at how to print a visual element with the WPF Visual Print. The essential part is serializing an XAML element to an XPS document, and converting the XPS document to a FlowDocument. Then we print and print preview the FlowDocument with the FlowDocument Viewer.
What is an XPS Document
The XML Paper Specification (XPS) format is basically an electronic representation of digital documents based on XML. It is a paginated fixed-layout format that retains the look and feel of your electronic documents. XPS documents can be easily created once you have the right software installed, like Microsoft Word.
The parts of an XPS document are organized in a logical hierarchy with the FixedDocumentSequence
part at the top. An XPS document package may contain more than one document, and the sequence of these documents is described by the FixedDocumentSequence
part. The FixedDocumentSequence
part references the FixedDocument
parts that, in turn, reference the pages of each document within the package.
Each FixedDocument
part references the pages of that document as FixedPage
parts. Each FixedPage
part contains the text markup and layout of a page in the document as well as references to images, fonts, and other custom resources used in the page. Resources such as images and fonts are stored in the package but outside of the FixedPage
part, allowing them to be shared by other pages. This is especially useful for font resources, but it could also be useful for any image resource that is used on more than one page, such as a watermark or letterhead logo.
I know it’s pretty boring for you to read these definitions. But I have to bring them out, because all these definitions will be used in the WPF Visual Print code.
Serialize a Visual Component to XPS Document
XPS documents are stored in a file, called a package, that conforms to the Open Packaging Conventions and are composed of a set of document components known as parts. A package has a physical and a logical organization. The physical organization consists of the document parts and folders inside the package, and the logical organization is a hierarchy described by the document parts. The XML Paper Specification applies a specific organization and naming convention to the logical layer for XPS documents.
WPF wraps the XPS API in the XPSSerializationManager
class. Thus WPF can create an XPS document by serializing the XAML element page to an XPS document. Here is our serialization code:
FrameworkElement fe = (visual as FrameworkElement);
fe.Measure(new Size(Int32.MaxValue, Int32.MaxValue));
Size visualSize = fe.DesiredSize;
fe.Arrange(new Rect(new Point(0, 0), visualSize));
MemoryStream stream = new MemoryStream();
string pack = "pack://temp.xps";
Uri uri = new Uri(pack);
DocumentPaginator paginator;
XpsDocument xpsDoc;
using (Package container = Package.Open(stream, FileMode.Create))
{
PackageStore.AddPackage(uri, container);
using (xpsDoc = new XpsDocument(container, CompressionOption.Fast, pack))
{
XpsSerializationManager rsm =
new XpsSerializationManager(new XpsPackagingPolicy(xpsDoc), false);
rsm.SaveAsXaml(visual);
paginator = ((IDocumentPaginatorSource)
xpsDoc.GetFixedDocumentSequence()).DocumentPaginator;
paginator.PageSize = visualSize;
}
PackageStore.RemovePackage(uri);
}
Custom Document Paginator
When you serialize a XAML element to an XPS document, it’s always one page. Apparently, it’s not good enough for visual elements that need multiple pages. So you need to write your own paginator class, VisualDocumentPaginator
.
The VisualDocumentPaginator
constructor modifies the page size of the original paginator based on the required page size and margin. The new GetPage
method calls the original GetPage
method to get a page, then tries to measure the visual element size and split it to multiple pages per page size. Here is our VisualDocumentPaginator
class:
public class VisualDocumentPaginator : DocumentPaginator
{
Size m_PageSize;
Size m_Margin;
DocumentPaginator m_Paginator = null;
int m_PageCount;
Size m_ContentSize;
ContainerVisual m_PageContent;
ContainerVisual m_SmallerPage;
ContainerVisual m_SmallerPageContainer;
ContainerVisual m_NewPage;
public VisualDocumentPaginator(DocumentPaginator paginator,
Size pageSize, Size margin)
{
m_PageSize = pageSize;
m_Margin = margin;
m_Paginator = paginator;
m_ContentSize = new Size(pageSize.Width - 2 * margin.Width,
pageSize.Height - 2 * margin.Height);
m_PageCount = (int)Math.Ceiling(m_Paginator.PageSize.Height /
m_ContentSize.Height);
m_Paginator.PageSize = m_ContentSize;
m_PageContent = new ContainerVisual();
m_SmallerPage = new ContainerVisual();
m_NewPage = new ContainerVisual();
m_SmallerPageContainer = new ContainerVisual();
}
Rect Move(Rect rect)
{
if (rect.IsEmpty)
{
return rect;
}
else
{
return new Rect(rect.Left + m_Margin.Width,
rect.Top + m_Margin.Height,
rect.Width, rect.Height);
}
}
public override DocumentPage GetPage(int pageNumber)
{
m_PageContent.Children.Clear();
m_SmallerPage.Children.Clear();
m_NewPage.Children.Clear();
m_SmallerPageContainer.Children.Clear();
DrawingVisual title = new DrawingVisual();
using (DrawingContext ctx = title.RenderOpen())
{
FontFamily font = new FontFamily("Times New Roman");
Typeface typeface =
new Typeface(font, FontStyles.Normal,
FontWeights.Bold, FontStretches.Normal);
FormattedText text = new FormattedText("Page " +
(pageNumber + 1) + " of " + m_PageCount,
System.Globalization.CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface, 14, Brushes.Black);
ctx.DrawText(text, new Point(0, 0));
}
DocumentPage page = m_Paginator.GetPage(0);
m_PageContent.Children.Add(page.Visual);
RectangleGeometry clip = new RectangleGeometry(
new Rect(0, m_ContentSize.Height * pageNumber,
m_ContentSize.Width, m_ContentSize.Height));
m_PageContent.Clip = clip;
m_PageContent.Transform =
new TranslateTransform(0, -m_ContentSize.Height * pageNumber);
m_SmallerPage.Children.Add(m_PageContent);
m_SmallerPage.Transform = new ScaleTransform(0.95,0.95);
m_SmallerPageContainer.Children.Add(m_SmallerPage);
m_SmallerPageContainer.Transform = new TranslateTransform(0, 24);
m_NewPage.Children.Add(title);
m_NewPage.Children.Add(m_SmallerPageContainer);
m_NewPage.Transform =
new TranslateTransform(m_Margin.Width, m_Margin.Height);
return new DocumentPage(m_NewPage, m_PageSize,
Move(page.BleedBox),Move(page.ContentBox));
}
public override bool IsPageCountValid
{
get
{
return true;
}
}
public override int PageCount
{
get
{
return m_PageCount;
}
}
public override Size PageSize
{
get
{
return m_Paginator.PageSize;
}
set
{
m_Paginator.PageSize = value;
}
}
public override IDocumentPaginatorSource Source
{
get
{
if (m_Paginator != null)
return m_Paginator.Source;
return null;
}
}
}
Here is the code which converts the default XPS document to a multiple pages XPS document:
using (Package container = Package.Open(stream, FileMode.Create))
{
using (xpsDoc = new XpsDocument(container, CompressionOption.Fast, pack))
{
paginator = new VisualDocumentPaginator(paginator,
new Size(pageSize.Width, pageSize.Height),
new Size(48, 48));
XpsSerializationManager rsm = new XpsSerializationManager(
new XpsPackagingPolicy(xpsDoc), false);
rsm.SaveAsXaml(paginator);
}
PackageStore.RemovePackage(uri);
}
Print and Print Preview with FlowDocument
Why We Use FlowDocument
A flow document is designed to "reflow content" depending on the window size, device resolution, and other environment variables. In addition, flow documents have a number of built-in features including search, viewing modes that optimize readability, and the ability to change the size and appearance of fonts. Flow documents are best utilized when ease of reading is the primary document consumption scenario.
Convert an XPS Document to a FlowDocument
There is an excellent article that talks about how to convert an XPS document to a flow document. I won’t repeat it here. As we talked before, an XPS document consists of multiple fixed pages. Every fixed page contains UI Element children. So in short words, this conversion extracts the UI Element children of a FixedPage
and adds them to a FlowDocument UI block.
One thing I need to bring out is, the font resource in an XPS document is obfuscated. These fonts normally are saved as ODTTF files. Before converting to a flowdocument, we need to de-obfuscate first. In .NET Framework 3.5, we still use the same name for the de-obfuscated font file to generate a Glyph. But in .NET Framework 4.0, you’ll get a null reference exception when you try to generate a Glyph with an ODTTF file, even this file is de-obfuscated. So you have to rename the de-obfuscated font file to a TTF file.
Shown below is the code to de-obfuscate an ODTTF font. You can get the GUID from the ODTT font file name:
private static void DeobfuscateData(byte[] fontData, Guid guid)
{
byte[] buffer = guid.ToByteArray();
for (int j = 0; j < 2; ++j)
{
for (int i = 0; i < 16; ++i)
{
fontData[i + (j * 16)] =
(byte)(fontData[i + (j * 16)] ^ buffer[15 - i]);
}
}
}
Print FlowDocument
Call the PrintDialog.PrintDocument
method to call the print dialog that will allow you to select a printer and send a document to the printer to print it. The PrintDocument
method of PrintDialog
takes a DocumentPaginator
object that you can get from the IDocumentPaginatorSource.DocumentPaginator
property as listed in the following code:
DocumentPaginator paginator =
((IDocumentPaginatorSource)m_FlowDocument).DocumentPaginator;
m_PrintDialog.PrintDocument(paginator, "Printing");
发表评论
I reckon something truly special in this website.
gTUmfC This excellent website really has all of the information I wanted concerning this subject and didn at know who to ask.
1SgZAZ It absolutely not agree with the previous message
MMmsJM Informative and precise Its hard to find informative and precise information but here I found
not everyone would need a nose job but my girlfriend really needs some rhinoplasty coz her nose is kind of crooked*
IxWrK8 You made a number of cloudless points near. I did a explore on the topic and found most personnel will commend with your website.
3OpqAp nfl jerseys than a toddler tea party. The boys are happy
njXC5r P.S Apologies for being off-topic but I had to ask!
SMczad Really informative article post.Thanks Again. Really Cool.
3Mwi31 Wow, marvelous blog layout! How long have you been blogging for? you made blogging look easy. The overall look of your web site is great, as well as the content!
bcfKsI Thanks for sharing, this is a fantastic article. Fantastic.
nZhEdj Im thankful for the blog.Really looking forward to read more. Awesome.
k4NHy9 Whispering Misty So sorry you can expect to skip the workshop!
PC34cE Very nice info and straight to the point. I am not sure if this is really the best place to ask but do you guys have any ideea where to hire some professional writers? Thanks
KTDmUG Thanks for sharing this great write-up. Very interesting ideas! (as always, btw)
9baSPY Pretty! This has been an incredibly wonderful post. Many thanks for providing this info.
9NMnkO Peculiar article, exactly what I needed.
6nnrhD web to learn more about the issue and found most people will go along with your views on this site.
pg6RTc wow, awesome article.Really looking forward to read more.
UaEyjJ This is my first time go to see at here and i am genuinely happy to read all at single place.
LUGOxr Thanks for sharing, this is a fantastic article. Great.
xxINpv Just Browsing While I was surfing today I saw a great article concerning
lH9rQa Very interesting details you have noted, thanks for posting.
DO9zKa if so then you will without doubt get good know-how. Here is my web blog; Led Lights
kOR4vu Really enjoyed this blog post.Really thank you! Fantastic.
uOfr2d this content Someone left me a comment on my blogger. I have clicked to publish the comment. Now I wish to delete this comment. How do I do that?..
rojQUz This is one awesome article post.Thanks Again. Keep writing.
There as certainly a great deal to find out about this topic. I love all the points you have made.
pacKg1 three triple credit report How hard is it to write a wordpress theme to fit into an existing site?
9kUiHS We all speak a little about what you should speak about when is shows correspondence to simply because Maybe this has much more than one meaning.
r6GsaY
ShaAv1 imp source I want to start selling hair bows. How do I get a website started and what are the costs?. How do I design it?.
AiYPJa Only a smiling visitant here to share the love (:, btw outstanding design. The price one pays for pursuing a profession, or calling, is an intimate knowledge of its ugly side. by James Arthur Baldwin.
gT64A3 Looking forward to reading more. Great article. Cool.
2Ix2QW Wholesale Cheap Handbags Will you be ok merely repost this on my site? I ave to allow credit where it can be due. Have got a great day!
eIMV0F It as amazing for me to have a web page, which is good in support of my knowledge. thanks admin
JaKNGl Useful information. Fortunate me I found your website by chance, and I'm surprised why this twist of fate didn't came about in advance! I bookmarked it.
aQT7Ib I am constantly invstigating online for ideas that can aid me. Thanks!
bXuzPD Heya i'm for the primary time here. I found this board and I to find It truly helpful & it helped me out a lot. I am hoping to offer one thing back and help others such as you aided me.
RLrt8i I just couldn't depart your site before suggesting that I really enjoyed the standard information a person provide for your visitors? Is gonna be back often to check up on new posts
LkoTYN Wow, fantastic blog layout! How long have you been blogging for? you make blogging look easy. The overall look of your site is fantastic, as well as the content!
xRp9t6 I really like and appreciate your blog article.Really looking forward to read more.
2yjDg9 I have been absent for a while, but now I remember why I used to love this web site. Thanks , I will try and check back more often. How frequently you update your website?
MdpKOi Very good blog post. Cool.
JdZAZc I cannot thank you enough for the post.Really thank you! Keep writing.
gZavE2 Very informative article.Much thanks again. Much obliged.
zpAqmW Thanks for sharing, this is a fantastic article.Really thank you! Want more.
Im719x wow, awesome blog.Thanks Again. Keep writing.
rr8pXc I really like and appreciate your blog. Much obliged.
l1AGuq I really like and appreciate your article. Want more.