Po pierwsze chciałbym podkreślić, że podniosłem to jako błąd w firmie Microsoft, ale nie są oni skłonni to naprawić w tym momencie. To, czego szukam, to obejście lub lepszy sposób osiągnięcia tego, co próbuję zrobić, ponieważ nasz klient uznał to za dość ważny problem.Powielone obrazy wydrukowane w pliku XPS
Kod
MainWindow.xaml
<Grid x:Name="mainGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Images}">
<ListBox.ItemTemplate>
<DataTemplate>
<Image Source="{Binding}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Print to file" Grid.Row="1" Click="PrintToFile_Click"/>
<Button Content="Print to device" Grid.Row="2" Click="PrintToDevice_Click"/>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public IList<byte[]> Images { get; set; }
public MainWindow()
{
InitializeComponent();
Assembly currentAssembly = Assembly.GetExecutingAssembly();
this.Images = new List<byte[]>
{
ReadToEnd(currentAssembly.GetManifestResourceStream("PrintingInvestigation.Images.Chrysanthemum.jpg")),
ReadToEnd(currentAssembly.GetManifestResourceStream("PrintingInvestigation.Images.Desert.jpg")),
ReadToEnd(currentAssembly.GetManifestResourceStream("PrintingInvestigation.Images.Hydrangeas.jpg")),
};
this.DataContext = this;
}
public static byte[] ReadToEnd(System.IO.Stream stream)
{
long originalPosition = 0;
if (stream.CanSeek)
{
originalPosition = stream.Position;
stream.Position = 0;
}
try
{
byte[] readBuffer = new byte[4096];
int totalBytesRead = 0;
int bytesRead;
while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
{
totalBytesRead += bytesRead;
if (totalBytesRead == readBuffer.Length)
{
int nextByte = stream.ReadByte();
if (nextByte != -1)
{
byte[] temp = new byte[readBuffer.Length * 2];
Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
readBuffer = temp;
totalBytesRead++;
}
}
}
byte[] buffer = readBuffer;
if (readBuffer.Length != totalBytesRead)
{
buffer = new byte[totalBytesRead];
Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
}
return buffer;
}
finally
{
if (stream.CanSeek)
{
stream.Position = originalPosition;
}
}
}
private void PrintToDevice_Click(object sender, RoutedEventArgs e)
{
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
{
Thickness pageMargins;
if (dialog.PrintTicket.PageBorderless.HasValue == true)
{
if (dialog.PrintTicket.PageBorderless.Value == PageBorderless.Borderless)
{
pageMargins = new Thickness(0, 0, 0, 0);
}
else
{
pageMargins = new Thickness(20, 20, 20, 20);
}
}
else
{
pageMargins = new Thickness(20, 20, 20, 20);
}
int dpiX = 300;
int dpiY = 300;
if (dialog.PrintTicket.PageResolution != null &&
dialog.PrintTicket.PageResolution.X.HasValue &&
dialog.PrintTicket.PageResolution.Y.HasValue)
{
dpiX = dialog.PrintTicket.PageResolution.X.Value;
dpiY = dialog.PrintTicket.PageResolution.Y.Value;
}
else
{
dialog.PrintTicket.PageResolution = new PageResolution(dpiX, dpiY);
}
VisualDocumentPaginator paginator = new VisualDocumentPaginator(this.mainGrid, this.mainGrid.ActualWidth);
paginator.PageSize = new Size(dialog.PrintableAreaWidth, dialog.PrintableAreaHeight);
dialog.PrintDocument(paginator, "My first print");
GC.Collect();
}
}
private void PrintToFile_Click(object sender, RoutedEventArgs e)
{
string filePath = this.PrintToFile(null, this.mainGrid, "My first print", this.mainGrid.ActualHeight, this.mainGrid.ActualWidth);
Process.Start(filePath);
}
public string PrintToFile(Visual titleVisual, Visual contentVisual, string title, double bottomMost, double rightMost)
{
string printedFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), string.Format(CultureInfo.InvariantCulture, "{0}.xps", title));
XpsDocument printedDocument = new XpsDocument(printedFilePath, FileAccess.Write, System.IO.Packaging.CompressionOption.SuperFast);
VisualDocumentPaginator paginator = new VisualDocumentPaginator(contentVisual as FrameworkElement, rightMost);
paginator.PageSize = new Size(793.7, 1122.5);
XpsDocumentWriter writer = XpsDocument.CreateXpsDocumentWriter(printedDocument);
writer.Write(paginator, new PrintTicket
{
Collation = Collation.Collated,
CopyCount = 1,
DeviceFontSubstitution = DeviceFontSubstitution.On,
Duplexing = Duplexing.OneSided,
InputBin = InputBin.AutoSelect,
OutputColor = OutputColor.Color,
OutputQuality = OutputQuality.High,
PageMediaSize = new PageMediaSize(PageMediaSizeName.ISOA4),
PageOrientation = PageOrientation.Portrait,
PageResolution = new PageResolution(PageQualitativeResolution.High),
PagesPerSheet = 1,
TrueTypeFontMode = TrueTypeFontMode.Automatic
});
printedDocument.Close();
return printedFilePath;
}
}
VisualDocumentPaginator.cs
public class VisualDocumentPaginator : DocumentPaginator
{
#region Fields
private double desiredWidth;
private FrameworkElement element;
#endregion
#region Properties
public int Columns
{
get
{
return 1;// (int)Math.Ceiling(Element.ActualWidth/PageSize.Width);
}
}
public int Rows
{
get
{
return (int)Math.Ceiling(element.ActualHeight/PageSize.Height);
}
}
#endregion
#region Constructors
public VisualDocumentPaginator(FrameworkElement element, double desiredWidth)
{
this.desiredWidth = desiredWidth;
this.element = element;
}
#endregion
#region DocumentPaginator Members
public override DocumentPage GetPage(int pageNumber)
{
TransformGroup transforms = new TransformGroup();
double scaleRatio = this.PageSize.Width/this.desiredWidth;
int row = (pageNumber/Columns);
double pageHeight = -PageSize.Height * row/scaleRatio;
double pageWidth = -PageSize.Width * (pageNumber % Columns);
transforms.Children.Add(new TranslateTransform(pageWidth, pageHeight));
// Make sure the control is stretched to fit the page size.
if (scaleRatio != double.NaN)
{
ScaleTransform st = new ScaleTransform(scaleRatio, scaleRatio);
transforms.Children.Add(st);
}
element.RenderTransform = transforms;
Size elementSize = new Size(this.desiredWidth, element.ActualHeight);
element.Measure(elementSize);
element.Arrange(new Rect(new Point(0, 0), elementSize));
var page = new DocumentPage(element, this.PageSize, new Rect(), new Rect());
element.RenderTransform = null;
return page;
}
public override bool IsPageCountValid
{
get { return true; }
}
public override int PageCount
{
get
{
return Columns * Rows;
}
}
public override Size PageSize { set; get; }
public override IDocumentPaginatorSource Source
{
get { return null; }
}
#endregion
}
Przepraszam za opublikowanie całego kodu, ale obejmuje on wszystkie obszary, w których widzę problem. Pomaga tutaj Microsoft bug report, do którego dołączony jest przykładowy projekt, w którym można odtworzyć problem.
Problem
Kwestia ta jest widoczna tylko podczas zapisu do pliku XPS gdzie tylko pierwszy obraz jest drukowany 3 razy, jeśli przycisk „Print to urządzenie” kliknięciu następnie poprawne obrazy są drukowane.
Powodem, dla którego wiążę bajt [], jest fakt, że moje obrazy są przechowywane w lokalnej bazie danych SQL CE. Przechowujemy je w bazie danych DB, ponieważ są one tylko małe ~ 2KB, a my pozwalamy użytkownikom na importowanie własnych ikon do systemu i chcemy mieć mechanizm gwarantujący, że nie zostaną przypadkowo usunięte.
UWAGA
Zauważyłem, że jeśli nie wiążą się z byte [], jak wspomniano powyżej, wtedy nie widzę problemu. Biorąc pod uwagę fakt, że system obecnie pracuje nad podejściem do przechowywania obrazów w DB wolałbym trzymać się go, jeśli istnieje obejście, jednak nie jestem całkowicie przeciwny zastąpieniu mechanizmu przechowywania tych obrazów.
dzięki za to. Miałem nadzieję znaleźć dość szybkie obejście problemu, ale klient zdecydował, że chce znieść obsługę XPS i przejść do formatu PDF, więc myślę, że będziemy przepisywać logikę drukowania, z której korzystamy. Z pewnością zajrzę do System.Drawing.Printing. – Bijington