title: Duplicate images printed in XPS file
body: <p>First off I would like to point out that I have raised this as a bug with Microsoft but they are unwilling to fix it at this point in time. What I am looking for is a workaround or a better way of achieving what I am trying to do as our customer has deemed this a rather important issue.</p>
  
  <p><strong>The code</strong></p>
  
  <p><strong>MainWindow.xaml</strong></p>
  
  <pre><code>&lt;Grid x:Name="mainGrid"&gt;
      &lt;Grid.RowDefinitions&gt;
          &lt;RowDefinition Height="Auto"/&gt;
          &lt;RowDefinition Height="Auto"/&gt;
          &lt;RowDefinition Height="Auto"/&gt;
      &lt;/Grid.RowDefinitions&gt;
      &lt;ListBox ItemsSource="{Binding Images}"&gt;
          &lt;ListBox.ItemTemplate&gt;
              &lt;DataTemplate&gt;
                  &lt;Image Source="{Binding}"/&gt;
              &lt;/DataTemplate&gt;
          &lt;/ListBox.ItemTemplate&gt;
      &lt;/ListBox&gt;
  
      &lt;Button Content="Print to file" Grid.Row="1" Click="PrintToFile_Click"/&gt;
      &lt;Button Content="Print to device" Grid.Row="2" Click="PrintToDevice_Click"/&gt;
  &lt;/Grid&gt;
  </code></pre>
  
  <p><strong>MainWindow.xaml.cs</strong></p>
  
  <pre><code>public partial class MainWindow : Window
  {
      public IList&lt;byte[]&gt; Images { get; set; }
  
      public MainWindow()
      {
          InitializeComponent();
  
          Assembly currentAssembly = Assembly.GetExecutingAssembly();
  
          this.Images = new List&lt;byte[]&gt;
          {
              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)) &gt; 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 &amp;&amp;
                  dialog.PrintTicket.PageResolution.X.HasValue &amp;&amp;
                  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;
      }
  }
  </code></pre>
  
  <p><strong>VisualDocumentPaginator.cs</strong></p>
  
  <pre><code>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
  }
  </code></pre>
  
  <p>Apologies for posting all the code but it covers all the areas in which I am seeing the issue. If it helps here is the <a href="https://connect.microsoft.com/VisualStudio/feedback/details/1105434/writing-wpf-visual-to-xps-document-duplicates-images">Microsoft bug report</a>  which has a sample project attached where the issue can be reproduced.</p>
  
  <p><strong>The problem</strong><br>
  The issue is only seen when writing to an XPS file where only the first image is printed 3 times, if the "Print to device" button is clicked then the correct images are printed.</p>
  
  <p>The reason why I am binding to a byte[] is because I am persisting my images in a local SQL CE database. We store them in a DB because they are only small ~2KB each plus we allow users to import their own Icons into the system to use and we wanted a mechanism to guarantee that they wouldn't be accidentally deleted.</p>
  
  <p><strong>NOTE</strong><br>
  I have noticed that if I do not bind to the byte[] as mentioned above then I do not see the issue. Given the fact that the system currently works off the approach of storing the images in a DB I would prefer to stick with it if there is workaround however I am not entirely against replacing the storage mechanism for these images.</p>