Extract a single page from an XPS document - c#

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

Related

print XPS from memory stream

I use c#
I need to print DOCX, I send the docx to server , convert it to XPS and return XPS as memory stream. convert docx by aspose.
At client I use next code:
System.IO.Stream docStream = ...any xps as stream;
Package package = Package.Open(docStream);
//Create URI for Xps Package
//Any Uri will actually be fine here. It acts as a place holder for the
//Uri of the package inside of the PackageStore
string inMemoryPackageName = string.Format("memorystream://{0}.xps", Guid.NewGuid());
Uri packageUri = new Uri(inMemoryPackageName);
//Add package to PackageStore
PackageStore.AddPackage(packageUri, package);
XpsDocument xpsDoc = new XpsDocument(package, CompressionOption.Maximum, inMemoryPackageName);
FixedDocumentSequence fixedDocumentSequence = xpsDoc.GetFixedDocumentSequence();
PrintDialog dlg = new PrintDialog();
XpsDocument xpsDoc = new
dlg.PrintDocument(fixedDocumentSequence .DocumentPaginator, "Document title");
PackageStore.RemovePackage(packageUri);
xpsDoc.Close();
It work excellent with english, but I print documents with hebrew and arabic too. And in hebrew and arabic order of words is correct, but print every word vice versa.
If I write memory stream on disk as XPS and open it , the text is correct.

Value of a string for file's location is nil but a stored value says it isn't

I'm trying to convert secured PDFs to XPS and back to PDF using FreeSpire and then combine them using iTextSharp. Below is my code snippet for converting various files.
char[] delimiter = { '\\' };
string WorkDir = #"C:\Users\*******\Desktop\PDF\Test";
Directory.SetCurrentDirectory(WorkDir);
string[] SubWorkDir = Directory.GetDirectories(WorkDir);
//convert items to PDF
foreach (string subdir in SubWorkDir)
{
string[] samplelist = Directory.GetFiles(subdir);
for (int f = 0; f < samplelist.Length - 1; f++)
{
if (samplelist[f].EndsWith(".doc") || samplelist[f].EndsWith(".DOC"))
{
Spire.Pdf.PdfDocument doc = new Spire.Pdf.PdfDocument();
doc.LoadFromFile(sampleist[f], FileFormat.DOC);
doc.SaveToFile((Path.ChangeExtension(samplelist[f],".pdf")), FileFormat.PDF);
doc.Close();
}
. //other extension cases
.
.
else if (samplelist[f].EndsWith(".pdf") || sampleList[f].EndsWith(".PDF"))
{
PdfReader reader = new PdfReader(samplelist[f]);
bool PDFCheck = reader.IsOpenedWithFullPermissions;
reader.Close();
if (PDFCheck)
{
Console.WriteLine("{0}\\Full Permisions", Loan_list[f]);
reader.Close();
}
else
{
Console.WriteLine("{0}\\Secured", samplelist[f]);
Spire.Pdf.PdfDocument doc = new Spire.Pdf.PdfDocument();
string path = Loan_List[f];
doc.LoadFromFile(samplelist[f]);
doc.SaveToFile((Path.ChangeExtension(samplelist[f], ".xps")), FileFormat.XPS);
doc.Close();
Spire.Pdf.PdfDocument doc2 = new Spire.Pdf.PdfDocument();
doc2.LoadFromFile((Path.ChangeExtension(samplelist[f], ".xps")), FileFormat.XPS);
doc2.SaveToFile(samplelist[f], FileFormat.PDF);
doc2.Close();
}
The issue is I get a Value cannot be null error in doc.LoadFromFile(samplelist[f]);.I have the string path = sampleList[f]; to check if samplelist[f] was empty but it was not. I tried to replace the samplelist[f] parameter with the variable named path but it also does not go though. I tested the PDF conversion on a smaller scale it it worked (see below)
string PDFDoc = #"C:\Users\****\Desktop\Test\Test\Test.PDF";
string XPSDoc = #"C:\Users\****\Desktop\Test\Test\Test.xps";
//Convert PDF file to XPS file
PdfDocument doc = new PdfDocument();
doc.LoadFromFile(PDFDoc);
doc.SaveToFile(XPSDoc, FileFormat.XPS);
doc.Close();
//Convert XPS file to PDF
PdfDocument doc2 = new PdfDocument();
doc2.LoadFromFile(XPSDoc, FileFormat.XPS);
doc2.SaveToFile(PDFDoc, FileFormat.PDF);
doc2.Close();
I would like to understand why I am getting this error and how to fix it.
There would be 2 solutions for the problem you are facing.
Get the Document in the Document Object not in PDFDocument. And then probably try to SaveToFile Something like this
Document document = new Document();
//Load a Document in document Object
document.SaveToFile("Sample.pdf", FileFormat.PDF);
You can use Stream for the same something like this
PdfDocument doc = new PdfDocument();
//Load PDF file from stream.
FileStream from_stream = File.OpenRead(Loan_list[f]);
//Make sure the Loan_list[f] is the complete path of the file with extension.
doc.LoadFromStream(from_stream);
//Save the PDF document.
doc.SaveToFile(Loan_list[f] + ".pdf",FileFormat.PDF);
Second approach is the easy one, but I would recommend you to use the first one as for obvious reasons like document will give better convertability than stream. Since the document have section, paragraph, page setup, text, fonts everything which need to be required to do a better or exact formatting required.

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.

OpenXml-SDK: How to apply FontFamily/Size to AltChunk of Type [TextPlain]

Can anybody show me how to apply Fontfamily/size to an AltChunk of Type
AlternativeFormatImportPartType.TextPlain
This is my Code, but I can´t figure out how to do this at all (even Google doesn´t help)
MainDocumentPart main = doc.MainDocumentPart;
string altChunkId = "AltChunkId" + Guid.NewGuid().ToString().Replace("-", "");
var chunk = main.AddAlternativeFormatImportPart
(AlternativeFormatImportPartType.TextPlain, altChunkId);
using (var mStream = new MemoryStream())
{
using (var writer = new StreamWriter(mStream))
{
writer.Write(value);
writer.Flush();
mStream.Position = 0;
chunk.FeedData(mStream);
}
}
var altChunk = new AltChunk();
altChunk.Id = altChunkId;
OpenXmlElement afterThat = null;
foreach (var para in main.Document.Body.Descendants<Paragraph>())
{
if (para.InnerText.Equals("Notizen:"))
{
afterThat = para;
}
}
main.Document.Body.InsertAfter(altChunk, afterThat);
if I do it this way I get "Courier New" with a Size of "10,5"
UPDATE
This is the working Solution I came up with:
Convert Plaintext to RTF, change the Fontfamily/size and apply it to the WordProcessingDocument!
public static string PlainToRtf(string value)
{
using (var rtf = new System.Windows.Forms.RichTextBox())
{
rtf.Text = value;
rtf.SelectAll();
rtf.SelectionFont = new System.Drawing.Font("Calibri", 10);
return rtf.Rtf;
}
}
var chunk = main.AddAlternativeFormatImportPart
(AlternativeFormatImportPartType.Rtf, altChunkId);
using (var mStream = new MemoryStream())
{
using (var writer = new StreamWriter(mStream))
{
var rtf = PlainToRtf(value);
writer.Write(rtf);
writer.Flush();
mStream.Position = 0;
chunk.FeedData(mStream);
}
}
//proceed with creating AltChunk and inserting it to the Document...
How to apply FontFamily/Size to AltChunk of Type [TextPlain]
I am afraid this is NOT possible, in any case, not with OpenXml SDK.
Why?
altChunk (Anchor for Imported External Content) object is further designed for importing content in the document. They are 'temporary' objects: it is a just a reference to an external content, that is incorporated "as is" in the document, and then, when the document will be opened and saved with Word, Word converts this external content in valid OpenXml content.
So you can't, for a newly created document, loop into the paragraphs in order to retrieve it and apply a style.
If you import rtf content for example, the style must be applied to rtf before importing it.
In case of plain text TextPlain (= Text file .txt), there is no style conversion (there is no style attached to the text file, you can change the font in NotePad, it will apply to all documents, this is an Application Level property).
And I can confirm that Word creates by default a style with "Courier New 10,5" to display the content of the file. I just tested.
What can I do?
Apply style after the document has been open/saved with Word. Note you will have to retreive the paragrap(s), or you could try to retrieve the style created in the document and change the font here. This link could help to achieve this:
How to: Apply a style to a paragraph in a word processing document (Open XML SDK).
Or maybe it exists(?) a registry key something Like this that you can change to change Word's default behavior on your computer. And even if it is, it doesn't solve the problem for newly created document which is opened the first time on the client.
Note from the OP:
I think a possible Solution to the Problem could be, converting the PlainText to RTF apply StyleInformation and then append it to WordProcessingDocument as AltChunk.
I totally agreed. Just note when he says apply StyleInformation, it means at rtf level.

Load XPS to documentviewer from embedded resource

i am trying to make help for my application. I have xps documents which i am loading to documentviewer. These files are embedded in resource file.
I am able to access these as bytearray.
For example
Properties.Resources.help_sudoku_methods_2
returns byte[]
However, documentviewer cant read it and requires fixeddocumentsequence.
So i create memory stream from bytearray, then xpsdocument and then fixeddocumentsequence like this:
private void loadDocument(byte[] sourceXPS)
{
MemoryStream ms = new MemoryStream(sourceXPS);
const string memoryName = "memorystream://ms.xps";
Uri memoryUri = new Uri(memoryName);
try
{
PackageStore.RemovePackage(memoryUri);
}
catch (Exception)
{ }
Package package = Package.Open(ms);
PackageStore.AddPackage(memoryUri, package);
XpsDocument xps = new XpsDocument(package, CompressionOption.SuperFast, memoryName);
FixedDocumentSequence fixedDocumentSequence = xps.GetFixedDocumentSequence();
doc.Document = fixedDocumentSequence;
}
This is very unclean aproach and also doesnt work if there are images in files - instead of images in new documents displays images from first loaded doc.
Is there any cleaner way to load XPS from embedded resources to documentviewer? or do i need somethink like copy file from resources to application directory and load from here and not memorystream? Thank you.
why dont you write file to system temp folder and then read from there.
Stream ReadStream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("file1.xps");
string tempFile = Path.GetTempPath()+"file1.xps";
FileStream WriteStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write);
ReadStream.CopyTo(WriteStream);
WriteStream.Close();
ReadStream.Close();
// Read tempFile INTO memory here and then
File.Delete(tempFile);

Categories

Resources