Merging XpsDocument for printing - wpf

I have an wpf application working on print function for merging multiple xps documents as single. when working it triggers error as "Cannot perform a read operation in write-only mode."
I have load data from memorystream
xpsDoc = new XpsDocument(pack, CompressionOption.SuperFast, inMemPackageName);
and the i have use merging reference
FixedDocumentSequence sourceSequence = doc.GetFixedDocumentSequence();
foreach (DocumentReference dr in sourceSequence.References)
{
DocumentReference newDocumentReference = new DocumentReference();
newDocumentReference.Source = dr.Source;
(newDocumentReference as IUriContext).BaseUri = (dr as IUriContext).BaseUri;
FixedDocument fd = newDocumentReference.GetDocument(true);
newDocumentReference.SetDocument(fd); seqNew.References.Add(newDocumentReference);
}
and then i have used this code to print
XpsDocumentWriterwriter=PrintQueue.CreateXpsDocumentWriter(printQueue);
DocumentPaginator paginator = seqNew.DocumentPaginator;
if (paginator.PageCount == MassPrintings.Count())
{
writer.Write(seqNew);
}
Please Suggest me a solution

Related

Display XPS Document in WPF

I need to display data from a database into a WPF app and save it as an XPS document. I want to display it as part of a main window (with Toolbar, Menu and StatusBar) and not as a new window.
What control(s) should I use? Currently, I am looking at FixedDocument and FlowDocument. Am I on the right track? Any good material on how to start?
Improving on Stehpen's answer above...
Assume you've added a documents folder to your project:
Create a method GetDocument where the GetFilePath method refers to the Folder/Filename in the folder above.
private void GetDocument()
{
string fileName = Environment.CurrentDirectory.GetFilePath("Documents\\Title.xps");
Debugger.Break();
XpsDocument doc = new XpsDocument(fileName, FileAccess.Read);
XDocViewer.Document = doc.GetFixedDocumentSequence();
}
Where GetFilePath is an extension method that looks like this:
public static class StringExtensions
{
public static string GetFilePath(
this string EnvironmentCurrentDirectory, string FolderAndFileName)
{
//Split on path characters
var CurrentDirectory =
EnvironmentCurrentDirectory
.Split("\\".ToArray())
.ToList();
//Get rid of bin/debug (last two folders)
var CurrentDirectoryNoBinDebugFolder =
CurrentDirectory
.Take(CurrentDirectory.Count() - 2)
.ToList();
//Convert list above to array for Join
var JoinableStringArray =
CurrentDirectoryNoBinDebugFolder.ToArray();
//Join and add folder filename passed in
var RejoinedString =
string.Join("\\", JoinableStringArray) + "\\";
var final = RejoinedString + FolderAndFileName;
return final;
}
}
Add a Document viewer in your XAML
And add this code in the cs file:
string fileName = null;
string appPath= System.IO.Path.GetDirectoryName(Assembly.GetAssembly(typeof(DocumentWindow)).CodeBase);
fileName = appPath + #"\Documents\Help.xps";
fileName = fileName.Remove(0, 6);
XpsDocument doc = new XpsDocument(fileName, FileAccess.Read);
docView.Document = doc.GetFixedDocumentSequence();

XPSDocumentWriter - Printing Specific Pages to Specific Trays

I'm currently working on a printing application. This app has the requirement that certain pages need to come from specific trays on the printer. Here's the guts of what I've got so far:
foreach (var dto in dispensersToPrint)
{
var documents = FilterDocumentSections(DispenserDocumentsToPrint.RetrieveByDispenserId(dto.DispenserId));
var groupedDocs = documents.GroupBy(t => t.DocumentTypeId);
var queueName = Properties.Settings.Default.PrinterName;
var queue = RawPrinterHelper.GetPrintQueue(queueName);
var seq = new FixedDocumentSequence();
var xpsWriter = PrintQueue.CreateXpsDocumentWriter(queue);
foreach (var docGroup in groupedDocs)
{
var printTicket = queue.DefaultPrintTicket.Clone();
var printTray = MapPrintTray((DocumentSectionType)docGroup.Key);
if (!printTray.IsNullOrEmpty())
{
printTicket = RawPrinterHelper.ModifyPrintTicket(printTicket, "psk:JobInputBin", printTray);
}
var fixedDoc = new FixedDocument();
fixedDoc.PrintTicket = printTicket;
foreach (var doc in docGroup)
{
var pageContent = new PageContent();
var fixedPage = new FixedPage();
var localFileName = string.Empty;
var unzippedFileName = string.Empty;
//copy files locally
localFileName = CopyFileToLocalMachine(doc.FileName);
//unzip file
unzippedFileName = EmfPrintingHelper.UnzipEmfFile(localFileName);
var itemToPrint = new PrintableEmfImage
{
DataContext = new EmfImageViewModel { FileName = unzippedFileName }
};
fixedPage.Children.Add(itemToPrint);
pageContent.Child = fixedPage;
fixedDoc.Pages.Add(pageContent);
}
var docRef = new DocumentReference();
docRef.SetDocument(fixedDoc);
seq.References.Add(docRef);
}
xpsWriter.Write(seq);
}
At a real high level:
For each Dispenser (Work Order) i need to print; i first start by grouping by the DocumentType (i.e. Print type A to tray 1)
I then create a new FixedDocumentSequence
For each DocumentType; I then create a fixed document. I then modify the print ticket to look at the appropriate tray.
I then build each individual page for each document type; and add them to the FixedDocument
Once the building of the FixedDocument is complete; I append it to the DocumentSequence.
I then send the FixedDocumentSequence to the xpsWriter.
But for some reason; these settings aren't being honored. I get all the documents printing out of the same tray.
Here are some of my observations so far:
The modifying of the print ticket does work; I've verified this by sending a modified printTicket into the xpsWriter; but this applies the settings to the entire job; which is a no go for me.
When querying my print capabilities; i noticed that i only have JobInputBin. I don't quite think this means this printer doesn't support the functionality; as multi-tray printing works from a similar WindowsForms app (which uses PageSettings.PaperSource)
Any ideas on what I could try next? Has anyone been successful doing something like this before?
I'll start off by saying, I don't have access to a printer with trays, so I am unfortunately not capable of testing this solution. That said, I'll direct your attention to an MSDN forum post, here, where the original poster was in pursuit of the same tray-per-page behavior.
Based on your posted code, you may have already seen some of what's in this post, judging by your posted code having at least some implementation of ModifyPrintTicket().
In the post, there are several different users, each citing a solution for their specific version of the problem. However, the one that seems most relevant in this case is the solution regarding namespaces not being correctly accounted for in ModifyPrintTicket() (as posted by
Jo0815). I say 'most relevant' because the poster speaks of the print tray being disregarded. They (wittersworld) provide an alternate implementation to correct the issue. In the post on MSDN, the link to the complete source is broken, but can be located here.
The gist is, on ModifyPrintTicket(), they add a namespaceUri parameter, then withing changed this:
if (node != null)
{
node.Attributes["name"].Value = newValue;
}
to this:
if (node != null)
{
if (newValue.StartsWith("ns0000"))
{
// add namespace to xml doc
XmlAttribute namespaceAttribute = xmlDoc.CreateAttribute("xmlns:ns0000");
namespaceAttribute.Value = namespaceUri;
xmlDoc.DocumentElement.Attributes.Append(namespaceAttribute);
}
node.Attributes["name"].Value = newValue;
}
allowing the user to specify the printer-specific namespace used.
I hope this is helpful.

Programatically add document to Hummingbird/OpenText eDocs database

I am working with the the (formerly Hummingbird Enterprise) OpenText eDocs document management system.
http://www.opentext.com/2/global/products/products-opentext-edocs-products/products-opentext-edocs-document-management.htm
We are still using Hummingbird 5.1.0.5.
I have been reviewing the API docs for this software, but some areas are slightly vague.
So far, I can create my Profile form, populate some values.
DOCSObjects.Application docApp = null;
DOCSObjects.IProfile profile = null;
Type fType = Type.GetTypeFromProgID("DOCSObjects.Application");
docApp = (DOCSObjects.Application)Activator.CreateInstance(fType);
try { profile = docApp.CurrentLibrary.CreateProfile("DEF_PROF"); }
catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message); }
if (profile != null)
{
try
{
profile.Columns["DOCNAME"].Value = "New PDF Document";
profile.Columns["APP_ID"].Value = "ACROBAT";
profile.ShowProfile(1);
// not sure how to set a document here
profile.SetDocument(docApp.CurrentLibrary.Name, document);
profile.Save(); // requires a short flag, but what?
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
}
else
{
MessageBox.Show("Profile is null");
}
Where I am having trouble is how to save a document with the profile.
I am using C# and the API docs and intellisense simply ask for on object for the document.
Does that mean the path or do I need to load the PDF into some specific DOCSObjects type?
Also, the API docs references a Constant such as OF_NORMAL when saving the document. I assume this is 0, but are there others I should know about? There are many Constants referenced in the docs that have no values defined. (All examples are in C++/VB).
I know it's a long shot anyone is using this software, but thought I would give it a try.
Thank you and any assistance is appreciated.
I have done it in VB - using an API wrapper that I created. You should use the PCDClient under DM API folder instead of the DOCSObjects.
This code here probably won't work right away for you because it is heavily customized, but play around with it and you can probably figure it out. Good Luck!
Public Sub CreateProfile(ByRef Doc As Profile)
Try
'SET THE STATIC META DATA
Doc.objDoc.SetProperty("TYPE_ID", "DOCS") ' DOCUMENT TYPE IS ALWAYS DOCS
Doc.objDoc.SetProperty("TYPIST_ID", RDIMSAPI._UserID)
Doc.objDoc.SetProperty("APP_ID", RDIMSData.GetApp(Doc.FileToImport)) ' FILE TO IMPORT
'CREATE THE DOCUMENT
Doc.objDoc.Create()
If Doc.objDoc.ErrNumber <> 0 Then
Throw New Exception(Doc.objDoc.ErrNumber & " - " & Doc.objDoc.ErrDescription)
End If
'RETRIEVE THE NEW DOCUMENT PROFILE
Dim DocNumber As Integer = Doc.objDoc.GetReturnProperty("%OBJECT_IDENTIFIER")
Dim VersionID As Integer = Doc.objDoc.GetReturnProperty("%VERSION_ID")
'ADD THE DOCUMENT TO THE PROFILE
Dim objPutDoc As New PCDClient.PCDPutDoc
objPutDoc.SetDST(RDIMSAPI._sDST)
objPutDoc.AddSearchCriteria("%TARGET_LIBRARY", RDIMSAPI._Library)
objPutDoc.AddSearchCriteria("%DOCUMENT_NUMBER", DocNumber)
objPutDoc.AddSearchCriteria("%VERSION_ID", VersionID)
objPutDoc.Execute()
If objPutDoc.ErrNumber <> 0 Then
Throw New Exception(Doc.objDoc.ErrNumber & " - " & Doc.objDoc.ErrDescription)
End If
objPutDoc.NextRow()
'UPLOAD THE DOCUMENT
Dim objPutStream As PCDClient.PCDPutStream = objPutDoc.GetPropertyValue("%CONTENT")
Dim fs As FileStream = System.IO.File.OpenRead(Doc.FileToImport)
Dim fi As FileInfo = New System.IO.FileInfo(Doc.FileToImport)
Dim br As BinaryReader = New BinaryReader(fs)
Dim addDocBytes As Byte() = br.ReadBytes(CInt(fs.Length))
br.Read(addDocBytes, 0, addDocBytes.Length)
br.Close()
Dim bytesWritten As Integer = 0
objPutStream.Write(addDocBytes, addDocBytes.Length, bytesWritten)
objPutStream.SetComplete()
'UNLOCK THE DOCUMENT
Dim objDoc As New PCDClient.PCDDocObject
objDoc.SetDST(RDIMSAPI._sDST)
objDoc.SetObjectType("0_RDIMSPROF_SYS")
objDoc.SetProperty("%TARGET_LIBRARY", RDIMSAPI._Library)
objDoc.SetProperty("%OBJECT_IDENTIFIER", DocNumber)
objDoc.SetProperty("%VERSION_ID", VersionID)
objDoc.SetProperty("%STATUS", "%UNLOCK")
objDoc.Update()
objDoc.Fetch()
objDoc = Nothing
If Doc.objDoc.ErrNumber <> 0 Then
Throw New Exception(Doc.objDoc.ErrNumber & " - " & Doc.objDoc.ErrDescription)
End If
'RELEASE ALL OBJECTS AND RETURN DOCUMENT NUMBER
objPutDoc = Nothing
Catch ex As Exception
'IF EXCEPTION, LOG ERROR AND DISPLAY MESSAGE
Throw New Exception("(" & Me.GetType().FullName & "." & New StackTrace(0).GetFrame(0).GetMethod.Name & ") " & ex.Message)
Exit Sub
End Try
End Sub
I don't know if you're still trying. But here's my C# code for this. It's part of a larger module, so it won't work immediately. The profile parameter would be for example "DEF_PROF".
This also uses the PCDClientLib. My understanding is that these are serverside libraries, wich you should use only on the server. And that you should use the lib you've already used for clientside code.
// All variable prepended with an underscore are class fields etc...
// DMImportException is a custom exception, nothing special really
/// <summary>
/// Import a file into the library previously logged in to.
/// </summary>
/// <param name="profile">The name of the used profile.</param>
/// <param name="profileNameValues">A dictionary of strings containing the profile values wich should be saved for the document.</param>
/// <param name="FileName">The path and filename of the file to import.</param>
public virtual void ImportFile(string profile, Dictionary<string, string> profileNameValues, string FileName)
{
if (!_isLoggedIn)
{
throw new DMImportException("Trying to import a file while not logged in into DM.");
}
int totalbyteswritten;
byte[] bdata;
bdata = file.readallbytes(filename);
pcddocobject objdoc = new pcddocobject();
objdoc.setproperty("%target_library", _library);
objdoc.setdst(_dst);
objdoc.setobjecttype(profile);
foreach(var profilenamevaluepair in profilenamevalues)
{
objdoc.setproperty(profilenamevaluepair.key, profilenamevaluepair.value);
}
objdoc.create();
if (objdoc.errnumber != 0)
{
throw new dmimportexception("error while creating a new objdoc. check the inner error.", objdoc.errnumber, objdoc.errdescription);
}
_docnumber = objDoc.GetReturnProperty("%OBJECT_IDENTIFIER").ToString();
_versionID = objDoc.GetReturnProperty("%VERSION_ID").ToString();
PCDPutDoc objPutDoc = new PCDPutDoc();
objPutDoc.SetDST(_dst);
objPutDoc.AddSearchCriteria("%TARGET_LIBRARY", _library);
objPutDoc.AddSearchCriteria("%DOCUMENT_NUMBER", _docNumber);
objPutDoc.AddSearchCriteria("%VERSION_ID", _versionID);
objPutDoc.Execute();
if (objPutDoc.ErrNumber != 0)
{
throw new DMImportException("RecentEdit Failure on Execute: Error while trying to get a handle to the newly created doc. Check the inner error.", objPutDoc.ErrNumber, objPutDoc.ErrDescription);
}
objPutDoc.NextRow();
PCDPutStream objPutStream = (PCDPutStream)objPutDoc.GetPropertyValue("%CONTENT");
objPutStream.Write((object)bdata, (int)bdata.Length, out TotalBytesWritten);
objPutStream.SetComplete();
objPutStream = null;
objDoc = null;
objDoc = new PCDDocObject();
objDoc.SetDST(_dst);
objDoc.SetObjectType(profile);
objDoc.SetProperty("%TARGET_LIBRARY", _library);
objDoc.SetProperty("%OBJECT_IDENTIFIER", _docNumber);
objDoc.SetProperty("%VERSION_ID", _versionID);
objDoc.SetProperty("%STATUS", "%UNLOCK");
objDoc.Update();
if (objDoc.ErrNumber != 0)
{
throw new DMImportException("Error while trying to unlock the just imported file. Check the inner error.", objDoc.ErrNumber, objDoc.ErrDescription);
}
objPutDoc = null;
objDoc = null;
return;
}
P.S. I'd recommend you update to a later version of eDocs (we're upgrading from 5.1.0.5 to 5.2.1 end of this week ;-D)
--- EDIT ---
I think you need
Application.CurrentLibrary.CreateProfile("PROF_DEF").CreateVersionFromFile( /* filePath is one of the params */);
if you really need to do this with the DM Ext. API instead of the DM API

How to deliver bitmap to the server?

Writing my first silverlight application.
I need to deliver some bitmap that the customer will choose ( used OpenFileDialog ) to the server side ( using web service ).
After the customer choosing the bitmap - i cant access the file and break hit to byte array because i dont see the file full path on the OpenFileDialog object properties.
How can i do it ?
( i have method that get Bitmap and return the bitmap as byte array )
I did that before, here is part of it:
OpenFileDialog dialog = new OpenFileDialog();
dialog.Filter = "Images (*.png; *.jpg)| *.png; *.jpg";
dialog.Multiselect = false;
if (dialog.ShowDialog() == true)
{
using (System.IO.Stream stream = dialog.File.OpenRead())
{
BinaryReader binaryReader = new BinaryReader(stream);
// here are the bytes you want, put them somewhere to send them to the server
byte[] imageBytes = binaryReader.ReadBytes((int)stream.Length);
// here is the filename if you need it
string filename = System.IO.Path.GetFileNameWithoutExtension(dialog.File.Name);
stream.Close();
}
}

Printing a WPF FlowDocument

I'm building a demo app in WPF, which is new to me. I'm currently displaying text in a FlowDocument, and need to print it.
The code I'm using looks like this:
PrintDialog pd = new PrintDialog();
fd.PageHeight = pd.PrintableAreaHeight;
fd.PageWidth = pd.PrintableAreaWidth;
fd.PagePadding = new Thickness(50);
fd.ColumnGap = 0;
fd.ColumnWidth = pd.PrintableAreaWidth;
IDocumentPaginatorSource dps = fd;
pd.PrintDocument(dps.DocumentPaginator, "flow doc");
fd is my FlowDocument, and for now I'm using the default printer instead of allowing the user to specify print options. It works OK, except that after the document prints, the FlowDocument displayed on screen has changed to to use the settings I specified for printing.
I can fix this by manually resetting everything after I print, but is this the best way? Should I make a copy of the FlowDocument before I print it? Or is there another approach I should consider?
yes, make a copy of the FlowDocument before printing it. This is because the pagination and margins will be different. This works for me.
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);
}
}
You can use the code from the URL below, it wraps the flow document in a fixed document and prints that, the big advantage is that you can use it to add margin, headers and footers.
https://web.archive.org/web/20150502085246/http://blogs.msdn.com:80/b/fyuan/archive/2007/03/10/convert-xaml-flow-document-to-xps-with-style-multiple-page-page-size-header-margin.aspx
The following works with both text and non-text visuals:
//Clone the source document
var str = XamlWriter.Save(FlowDoc);
var stringReader = new System.IO.StringReader(str);
var xmlReader = XmlReader.Create(stringReader);
var CloneDoc = XamlReader.Load(xmlReader) as FlowDocument;
//Now print using PrintDialog
var pd = new PrintDialog();
if (pd.ShowDialog().Value)
{
CloneDoc.PageHeight = pd.PrintableAreaHeight;
CloneDoc.PageWidth = pd.PrintableAreaWidth;
IDocumentPaginatorSource idocument = CloneDoc as IDocumentPaginatorSource;
pd.PrintDocument(idocument.DocumentPaginator, "Printing FlowDocument");
}
I am also generating a WPF report off a Flow document, but I am purposely using the flow document as a print preview screen. I there for want the margins to be the same. You can read about how I did this here.
In your scenario I'm thinking why not just make a copy of your settings, instead of the entire flow document. You can then re-apply the settings if you wish to return the document back to it's original state.

Resources