Can multiple xps documents be merged to one in WPF? - wpf

Can multiple xps documents be merged to one xps document in WPF and shown in DocumentViewer?
An application has 4 small xps documents each displayed seperately, but in one of the places all 4 documents should be shown as one document. How do I go about it?

Here, targetDocument is the target path of the new file and list is a list of all documents to be merged.
public void CreateXPSStreamPages(string targetDocument, List<string> list)
{
Package container = Package.Open(targetDocument, FileMode.Create);
XpsDocument xpsDoc = new XpsDocument(container);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(xpsDoc);
SerializerWriterCollator vxpsd = writer.CreateVisualsCollator();
vxpsd.BeginBatchWrite();
foreach (string sourceDocument in list)
{
AddXPSDocument(sourceDocument, vxpsd);
}
vxpsd.EndBatchWrite();
container.Close();
}
public void AddXPSDocument(string sourceDocument, SerializerWriterCollator vxpsd)
{
XpsDocument xpsOld = new XpsDocument(sourceDocument, FileAccess.Read);
FixedDocumentSequence seqOld = xpsOld.GetFixedDocumentSequence();
foreach (DocumentReference r in seqOld.References)
{
FixedDocument d = r.GetDocument(false);
foreach (PageContent pc in d.Pages)
{
FixedPage fixedPage = pc.GetPageRoot(false);
double width = fixedPage.Width;
double height = fixedPage.Height;
Size sz = new Size(width, height);
fixedPage.Width = width;
fixedPage.Height = height;
fixedPage.Measure(sz);
fixedPage.Arrange(new Rect(new Point(), sz));
//fixedPage.UpdateLayout();
ContainerVisual newPage = new ContainerVisual();
newPage.Children.Add(fixedPage);
//test: add Watermark from Feng Yuan sample
//newPage.Children.Add(CreateWatermark(width, height, "Watermark"));
vxpsd.Write(newPage);
}
}
xpsOld.Close();
}

In case someone is interested in the VB code:
Public Sub CreateXPSStream(targetDocument As String, ListToMerge As List(Of String))
If (File.Exists(targetDocument)) Then
File.Delete(targetDocument)
End If
Dim container As Package = Package.Open(targetDocument, FileMode.Create)
Dim xpsDoc = New System.Windows.Xps.Packaging.XpsDocument(container)
Dim seqNew As FixedDocumentSequence = New FixedDocumentSequence()
For Each sourceDocument As String In ListToMerge
AddXPSDocuments(sourceDocument, seqNew)
Next
Dim xpsWriter As XpsDocumentWriter = XpsDocument.CreateXpsDocumentWriter(xpsDoc)
xpsWriter.Write(seqNew)
xpsDoc.Close()
container.Close()
End Sub
Public Sub AddXPSDocuments(ByVal sourceDocument As String, ByRef seqNew As FixedDocumentSequence)
Try
Dim xpsOld As XpsDocument = New XpsDocument(sourceDocument, FileAccess.Read)
Dim seqOld As FixedDocumentSequence = xpsOld.GetFixedDocumentSequence()
For Each r As DocumentReference In seqOld.References
Dim newRef As DocumentReference = New DocumentReference()
CType(newRef, IUriContext).BaseUri = CType(r, IUriContext).BaseUri
newRef.Source = r.Source
seqNew.References.Add(newRef)
Next
Catch ex As Exception
myStatusAdd("Error with " & sourceDocument)
End Try
End Sub
thanks for the initial code.
This requires also a fair amount of references in your projects:
PresentationCore
PresentationFramework
ReachFramwork
System.Core
System.Windows.Presentation
System.Xaml
System.Printing
WindowsBase
I think that's all.

Related

WPF Dynamic image generation and printing is not working

In an application I'm developing, the user can select a number of items (max. 6) in a datagrid. These items should then be printed in a matrix style on pre-printed forms (see it like CD labels, 6 on a page).
I'm generating these images dynamically containing the selected content from a database. I then put these in a grid so they can be printed on the pre-printed forms.
I have the following code that creates the grid, generates the images from a user control and adds them to the grid and then prints these images.
I'm not following the MVVM pattern for the printing action. I created a reference to my view model in my code-behind.
Private Sub PrintButton_Click(sender As Object, e As RoutedEventArgs) Handles ButtonPrint.Click
Dim dlg As New PrintDialog
dlg.PrintTicket.PageMediaSize = New PageMediaSize(PageMediaSizeName.ISOA4)
Dim pageWidth As Double = GetPageWidth(dlg)
'Since the label is a perfect square: labelWidth = labelHeight
Dim labelWidthInPx As Integer = Utilities.ConvertMmToPixels(My.Settings.LabelWidthInMm)
'Set spacing distances in pixels
Dim horizontalLabelSpacing As Integer = Utilities.ConvertMmToPixels(My.Settings.HorizontalLabelSpacinginMm)
Dim verticalLabelSpacing As Integer = Utilities.ConvertMmToPixels(My.Settings.VerticalLabelSpacingInMm)
Dim topMargin As Integer = Utilities.ConvertMmToPixels(My.Settings.TopMarginInMm)
Dim leftMargin As Integer = Utilities.ConvertMmToPixels(My.Settings.LeftMarginInMm)
Dim bottomMargin As Integer = Utilities.ConvertMmToPixels(My.Settings.BottomMarginInMm)
'Create the table/grid
Dim tbl As New Grid
If CheckBoxPrintGridLines.IsChecked Then
tbl.ShowGridLines = True
Else
tbl.ShowGridLines = False
End If
'Add 3 columns (2 for the labels, 1 for spacing)
Dim col1 As New ColumnDefinition With {
.Width = New GridLength(labelWidthInPx, GridUnitType.Pixel)
}
Dim col2 As New ColumnDefinition With {
.Width = New GridLength(horizontalLabelSpacing, GridUnitType.Pixel)
}
Dim col3 As New ColumnDefinition With {
.Width = New GridLength(labelWidthInPx, GridUnitType.Pixel)
}
tbl.ColumnDefinitions.Add(col1)
tbl.ColumnDefinitions.Add(col2)
tbl.ColumnDefinitions.Add(col3)
'Add 5 Rows (3 for labels, 2 for spacing)
Dim row1 As New RowDefinition With {
.Height = New GridLength(labelWidthInPx, GridUnitType.Pixel)
}
Dim row2 As New RowDefinition With {
.Height = New GridLength(verticalLabelSpacing, GridUnitType.Pixel)
}
Dim row3 As New RowDefinition With {
.Height = New GridLength(labelWidthInPx, GridUnitType.Pixel)
}
Dim row4 As New RowDefinition With {
.Height = New GridLength(verticalLabelSpacing, GridUnitType.Pixel)
}
Dim row5 As New RowDefinition With {
.Height = New GridLength(labelWidthInPx, GridUnitType.Pixel)
}
tbl.RowDefinitions.Add(row1)
tbl.RowDefinitions.Add(row2)
tbl.RowDefinitions.Add(row3)
tbl.RowDefinitions.Add(row4)
tbl.RowDefinitions.Add(row5)
'Add label images
Dim reelData = CType(DataGridReels.ItemsSource, List(Of ReelInfo))
Dim rowIndex As Integer = 0
Dim colIndex As Integer = 0
For Each reel In reelData.Where(Function(r) r.IsSelected = True)
Dim partNumberData = ViewModel.DataService.GetPartNumberDataAsync(reel.PartNumber)
Dim batchData = ViewModel.DataService.GetBatchDataAsync(reel.PartNumber, reel.HENBatchNumber)
LabelImageControl.IsPrinting = True
LabelImageControl.PartNumberData = partNumberData.Result
LabelImageControl.BatchData = batchData.Result
LabelImageControl.ReelData = reel
LabelImageControl.Refresh
UpdateUI()
Dim labelImage As New Image
labelImage = GetImageFromLabel(LabelImageControl)
labelImage.Width = labelWidthInPx
labelImage.Height = labelWidthInPx
Grid.SetRow(labelImage, rowIndex)
Grid.SetColumn(labelImage, colIndex)
tbl.Children.Add(labelImage)
tbl.Refresh
colIndex += 1
If colIndex > 1 Then
colIndex = 0
rowIndex += 1
End If
Next
Dim iuc As New InlineUIContainer(tbl)
Dim p As New Paragraph(iuc)
'Create print dialog
If dlg.ShowDialog.GetValueOrDefault Then
'Create a flow document
Dim doc As New FlowDocument With {
.Name = "LabelPage",
.ColumnWidth = pageWidth,
.PagePadding = New Thickness(leftMargin, topMargin, 0, bottomMargin)
}
doc.Blocks.Add(p)
'Create IDocumentPagniatorSource from FlowDocument
Dim idpSource As IDocumentPaginatorSource = doc
Try
Me.Cursor = Cursors.Wait
dlg.PrintDocument(idpSource.DocumentPaginator, "Label Page")
Catch ex As Exception
Me.Cursor = Cursors.Arrow
MessageBox.Show("An error occurred during printing: " & ex.Message, "Print error")
Finally
Me.Cursor = Cursors.Arrow
End Try
End If
End Sub
When I send the output to the PDF printer I only get one image in the top left corner of the page/grid.
Since the user control (LabelImageControl) is in the XAML, I can see it changing while debugging. So the data is coming into the user control correctly.
When I check the grid with the XML Visualiser I see it has the same number of children as the items I selected in the datagrid.
Can anyone point me in the right direction on how to get the grid printing correctly?

The image is missing a frame. COMException (0x88982F62)

We have the following popup in a WPF application that should display two images returned from the internet, but throws the toys out with 'The image is missing a frame' when it hits EndInit()
Never come across this error before and can't find any explanation online that relates to this use - could it be an issue with the downloaded image, and if so how do I check it?
Thank you
Public Sub PopupModals_ChequeImages(ImageOne() As Byte, ImageTwo() As Byte)
Try
MainPopUp = New Window
With MainPopUp
.Width = 800
.Height = 750
.ResizeMode = ResizeMode.NoResize
.Title = "Check Images"
.Icon = BitmapFrame.Create(ReturnIconImage("GIF_ICO.ico"))
End With
MainPopUpGrid = New Grid
NameScope.SetNameScope(MainPopUpGrid, New NameScope())
Dim vGrid As New Grid
For i As Integer = 0 To 2
Dim vRow As New RowDefinition
If i = 2 Then
vRow.Height = New GridLength(35)
Else
vRow.Height = New GridLength(35, GridUnitType.Star)
End If
MainPopUpGrid.RowDefinitions.Add(vRow)
Next
Dim UpperSV As New ScrollViewer
With UpperSV
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
End With
Grid.SetRow(UpperSV, 0)
MainPopUpGrid.Children.Add(UpperSV)
Dim LowerSV As New ScrollViewer
With LowerSV
.VerticalScrollBarVisibility = ScrollBarVisibility.Auto
End With
Grid.SetRow(LowerSV, 1)
MainPopUpGrid.Children.Add(LowerSV)
'Convert the files and load into the scrollviewers'
Dim vImage1 As New Image
Dim vBitmap As New BitmapImage
Using vStream As New IO.MemoryStream(ImageOne)
With vBitmap
.BeginInit()
.CreateOptions = BitmapCreateOptions.PreservePixelFormat
.CacheOption = BitmapCacheOption.OnLoad
.StreamSource = vStream
.EndInit()
.Freeze()
End With
vImage1.Source = vBitmap
End Using
UpperSV.Content = vImage1
Dim vImage2 As New Image
vBitmap = New BitmapImage
Using vStream As New IO.MemoryStream(ImageTwo)
With vBitmap
.BeginInit()
.CreateOptions = BitmapCreateOptions.PreservePixelFormat
.CacheOption = BitmapCacheOption.OnLoad
.StreamSource = vStream
.EndInit()
.Freeze()
End With
vImage2.Source = vBitmap
End Using
LowerSV.Content = vImage2
Dim DP As DockPanel = PopupStatusBar()
Grid.SetRow(DP, 2)
MainPopUpGrid.Children.Add(DP)
StatusBarLoaded("Check images...")
MainPopUp.Content = MainPopUpGrid
MainPopUp.WindowStartupLocation = WindowStartupLocation.CenterOwner
Dim CurApp As Application = Application.Current
Dim vWindow As Window = CurApp.MainWindow
MainPopUp.Owner = vWindow
MainPopUp.ShowDialog()
Catch ex As Exception
EmailError(ex)
End Try
End Sub
Full exception is
The image is missing a frame.
at System.Windows.Media.Imaging.BitmapDecoder.SetupDecoderFromUriOrStream(Uri uri, Stream stream, BitmapCacheOption cacheOption, Guid& clsId, Boolean& isOriginalWritable, Stream& uriStream, UnmanagedMemoryStream& unmanagedMemoryStream, SafeFileHandle& safeFilehandle)
at System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
at System.Windows.Media.Imaging.BitmapImage.FinalizeCreation()
at System.Windows.Media.Imaging.BitmapImage.EndInit()
at HOAManagerClient051.PopupModals.PopupModals_ChequeImages(Byte[] ImageOne, Byte[] ImageTwo) in C:\Users\Dave\Documents\Visual Studio 2017\Projects\HOAManagerClient051\HOAManagerClient051\PopupModals.vb:line 3125
System.Runtime.InteropServices.COMException (0x88982F62): Exception from HRESULT: 0x88982F62
Images are returned like this
Await Task.Run(Sub()
Using response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
Dim deserializer As New DataContractJsonSerializer(GetType(AllianceBank.CheckImageResponse))
checkResponseObject = DirectCast(deserializer.ReadObject(response.GetResponseStream()), AllianceBank.CheckImageResponse)
End Using
End Sub)
Dim frontImageRawGif As Byte() = Nothing
Dim backImageRawGif As Byte() = Nothing
Dim IsCheckImage As Boolean = True
Try
frontImageRawGif = Convert.FromBase64String(checkResponseObject.FrontImage)
Catch ex As Exception
IsCheckImage = False
End Try
Try
backImageRawGif = Convert.FromBase64String(checkResponseObject.BackImage)
Catch ex As Exception
IsCheckImage = False
End Try

Convert high DPI images to lower DPI for printing throws OutOfMemoryException

I have some images that I'am trying to print out. Those images can come in varying format, from different DPI's to different formats (JPEG, PNG, etc.)
Now what I've done for now, is to load the image into my application and try and
convert the dpi to say 96. However in this process i get an OutOfMemoryException, and I'm not sure how to continue.
Private Sub PrintImage(Optional providedPrintDialog As PrintDialog = Nothing)
Dim objPrintDialog As PrintDialog
If providedPrintDialog IsNot Nothing Then
objPrintDialog = providedPrintDialog
Else
objPrintDialog = New PrintDialog()
End If
Dim myPanel As New StackPanel
myPanel.Margin = New Thickness(15)
Dim myImage As New Controls.Image
Dim tempBitmapImage = ConvertBitmapToXDPI(Me.SelectedFileViewModel.File.GetPath, 96)
Dim tempBitmapImageWidth As Integer = CInt(objPrintDialog.PrintableAreaWidth)
' A4 max width = 793
If tempBitmapImage.Width > tempBitmapImageWidth Then
myImage.Stretch = System.Windows.Media.Stretch.Uniform
Else
myImage.Stretch = System.Windows.Media.Stretch.None
End If
myImage.Source = tempBitmapImage
myPanel.Children.Add(myImage)
myPanel.Measure(New System.Windows.Size(objPrintDialog.PrintableAreaWidth, objPrintDialog.PrintableAreaHeight))
myPanel.Arrange(New Rect(New System.Windows.Point(0, 0), myPanel.DesiredSize))
objPrintDialog.PrintVisual(myPanel, "Billede") ' <- OutOfMemoryException thrown here
End Sub
Private Function ConvertBitmapToXDPI(path As String, newDpi As Integer) As BitmapSource
Using bitmap As Bitmap = DirectCast(System.Drawing.Image.FromFile(path), Bitmap)
Dim bitmapData = bitmap.LockBits(New System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.[ReadOnly], bitmap.PixelFormat)
Dim bmSource = BitmapSource.Create(
bitmapData.Width,
bitmapData.Height, 96, 96, PixelFormats.Bgr24, Nothing,
bitmapData.Scan0,
bitmapData.Stride * bitmapData.Height,
bitmapData.Stride)
bitmap.UnlockBits(bitmapData)
Return bmSource
End Using
End Function
There is no need to do any DPI conversion. Just create a DrawingVisual and draw a BitmapImage into it with an appropriate size:
Dim image As New BitmapImage()
image.BeginInit()
image.CacheOption = BitmapCacheOption.OnLoad
image.UriSource = New Uri(path)
image.EndInit()
image.Freeze()
Dim size As New Size()
If image.Width < printDialog.PrintableAreaWidth Then
size.Width = image.Width
size.Height = image.Height
Else
size.Width = printDialog.PrintableAreaWidth
size.Height = size.Width / image.Width * image.Height
End If
Dim visual As New DrawingVisual()
Using dc As DrawingContext = visual.RenderOpen()
dc.DrawImage(image, New Rect(size))
End Using
printDialog.PrintVisual(visual, "Billede")

How image included within the text in RichBox in WPF?

When inside the tool I want to text included in the image between the place where I want to speak, as the following picture:
enter image description here
I tried the following code but the photo does not show the speech, but end of the sentence appears:
Dim para As New Paragraph()
Dim bitmap As New BitmapImage(New Uri("D:\Happy.png"))
Dim image As New Image()
image.Source = bitmap
image.Width = 20
para.Inlines.Add(image)
RTB.Document.Blocks.Add(para)
See this link for examples Inline Images or Other Elements.
'A RichTextBox with an image.
Private Sub ImageRTB()
'Create a new RichTextBox.
Dim MyRTB As New RichTextBox()
' Create a Run of plain text and image.
Dim myRun As New Run()
myRun.Text = "Displaying text with inline image"
Dim MyImage As New Image()
MyImage.Source = New BitmapImage(New Uri("flower.jpg", UriKind.RelativeOrAbsolute))
MyImage.Height = 50
MyImage.Width = 50
Dim MyUI As New InlineUIContainer()
MyUI.Child = MyImage
' Create a paragraph and add the paragraph to the RichTextBox.
Dim myParagraph As New Paragraph()
MyRTB.Blocks.Add(myParagraph)
' Add the Run and image to it.
myParagraph.Inlines.Add(myRun)
myParagraph.Inlines.Add(MyUI)
'Add the RichTextBox to the StackPanel.
MySP.Children.Add(MyRTB)
End Sub
The solution found and this is the code:
Dim tp As TextPointer = rtb.CaretPosition.GetInsertionPosition(LogicalDirection.Forward)
Dim bm As New BitmapImage()
bm.BeginInit()
bm.UriSource = New Uri("Happy.png", UriKind.Relative)
bm.CacheOption = BitmapCacheOption.OnLoad
bm.EndInit()
Dim img As New Image()
img.Source = bm
img.Width = 20
img.Height = 20
img.Stretch = Stretch.Fill
Dim container As New InlineUIContainer(img, tp)
thank you :)

WPF Image Path Blues

I have a number of images in the resources directory and access them like this...
Public Function ReturnToolBarImage(ByVal ImageName As String) As Image
Dim UpdateImage As New Image
With UpdateImage
End With
Dim UpdateBitmap As New BitmapImage
With UpdateBitmap
.BeginInit()
.UriSource = New Uri("pack://application:,,,/HOA_Manager_Client_04;component/Resources/" & ImageName, UriKind.Absolute)
.EndInit()
.DecodePixelHeight = 32
End With
UpdateImage.Source = UpdateBitmap
Return UpdateImage
End Function
I have control that insists upon using an Image Path not an Image - I can access it like this
.ImageSource = "//application:,,,/HOA_Manager_Client_04;component/Resources/Customers.png"
But the image is, or course, far too large.. Soooooo I added a function to save the resized image to a Temp folder (that is working) and send the path back - but for some reason that just doesn't work (no errors, just no image). Any ideas?
Public Function ReturnToolBarImageAsString(ByVal ImageName As String) As String
Dim UpdateImage As New Image
With UpdateImage
End With
Dim UpdateBitmap As New BitmapImage
With UpdateBitmap
.BeginInit()
.UriSource = New Uri("pack://application:,,,/HOA_Manager_Client_04;component/Resources/" & ImageName, UriKind.Absolute)
.EndInit()
.DecodePixelHeight = 32
End With
Using FS As New IO.FileStream("../../Pages/Temp/" & ImageName, IO.FileMode.Create)
Dim vEncoder As New PngBitmapEncoder
vEncoder.Frames.Add(BitmapFrame.Create(UpdateBitmap))
vEncoder.Save(FS)
End Using
'Return "../Temp/" & ImageName
Return "//application:,,,/HOA_Manager_Client_04;component/Pages/Temp/" & ImageName
End Function
By creating a directory at run time in the Bin directory seems to have cured the issue
Public Function ReturnToolBarImageAsString(ByVal ImageName As String) As String
Dim UpdateImage As New Image
With UpdateImage
End With
Dim UpdateBitmap As New BitmapImage
With UpdateBitmap
.BeginInit()
.DecodePixelHeight = 32
.UriSource = New Uri("pack://application:,,,/HOA_Manager_Client_04;component/Resources/" & ImageName, UriKind.Absolute)
.EndInit()
End With
Dim vPath As String = My.Application.Info.DirectoryPath
If Not IO.Directory.Exists(vPath & "\Temp_Images") Then
IO.Directory.CreateDirectory(vPath & "\Temp_Images")
End If
Using FS As New IO.FileStream(vPath & "\Temp_Images\" & ImageName, IO.FileMode.Create)
Dim vEncoder As New PngBitmapEncoder
vEncoder.Frames.Add(BitmapFrame.Create(UpdateBitmap))
vEncoder.Save(FS)
End Using
Return vPath & "\Temp_Images\" & ImageName
End Function

Resources