2015-05-29 18 views
6

İlk önce Microsoft ile ilgili bir hata olduğunu belirtmek isterim ancak bu noktada düzeltmeyi istemiyorlar. Aradığım şey, müşterimizin bu kadar önemli bir konu olduğunu düşündüğüm şeyi yapmaya çalışmanın bir geçici çözümü veya daha iyi bir yoludur.XPS dosyasında yazdırılan görüntülerin kopyalanması

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 
} 

tüm kodu yazabilmek için Özürler ama ben konuyu görüyorum ki bütün alanları kapsamaktadır. Burada yardımcı olursa, sorunun yeniden üretilebileceği bir örnek projeye sahip olan Microsoft bug report.

"yazdır cihaza" butonuna ardından tıklandığında doğru görüntüler yazdırılır ise, sadece ilk görüntü 3 kez basılır bir XPS dosyası yazarken
sorun yalnızca görülür sorunu.

Bir bayt [] ile bağlandığımın nedeni, görüntüleri yerel bir SQL CE veritabanında ısrar etmemin nedeni. Bunları bir DB'de saklıyoruz, çünkü her biri yalnızca 2 ~ 2KB değerindedir ve kullanıcıların kendi Simgelerini sisteme aktarmalarına izin veriyoruz ve yanlışlıkla silinmeyeceklerini garanti etmek için bir mekanizma istedik.

NOT
ben byte bağlamak yoksa [] yukarıda belirtildiği gibi o zaman sorunu görmüyorum fark etmiş. Sistemin görüntülerin bir DB'de saklanması yaklaşımıyla çalıştığı göz önüne alındığında, geçici bir çözüm varsa onunla uğraşmayı tercih ederim, ancak bu görüntüler için depolama mekanizmasının yerini tamamen almamaktayım.

cevap

1

WPF'de yerleşik bir belge yönetim sistemi için özel bir yazdırma çözümü oluşturdum. System.Printing ad alanını kullanmaya başladım, ancak .NET'te Microsoft'un uzun zamandır çözmediği sonsuz hatalar buldum. Mümkün olan hiçbir geçici çözüm ile, hiçbir sorun bulamadığım Windows Forms için oluşturulmuş daha olgun System.Drawing.Printing ad alanını kullanarak sona erdi. Kodunuzu System.Drawing.Printing'a yeniden yazmanız biraz zaman alacaktır, ancak yol boyunca bir tuğla duvara çarpma olasılığınız çok düşüktür.

+0

bunun için teşekkürler. Soruna oldukça hızlı bir çözüm bulmayı umuyordum, ancak müşteri şimdi XPS desteğini düşürmek ve PDF'ye geçmek istediğine karar verdiğinden, kullandığımız baskı mantığını yeniden yazacağımızı düşünüyorum. Ben kesinlikle System.Drawing.Printing içine bakacağız. – Bijington

1

İlk görüntünün çoğaltıldığı ve diğer tüm resimlerin değiştirildiği benzer bir sorunu yaşadım. Benim durumumda, bir cihaza, bir XPS belgesine veya PDF belgesine yazdırmak önemli değildi, sorun hala oradaydı.

ben sorunu benim kodunda veya genel kodunda ise görüntülerle System.Windows.Xps.XpsDocumentWriter sınıf fırsatlar bulmak için nasıl anlamaya bir .NET derlemesi decompiler kullandık

analizi. Çerçevenin, görüntüler gibi kaynakları belgeye içe aktarmak için kullanılan dictionnairies olduğunu keşfettim.Görüntüler XPS belgesinde yalnızca bir kez içe aktarılsa bile, belgeye birçok kez başvurma izni verilir.

Durumumda, sorunun System.Windows.Xps.Serialization.ImageSourceTypeConverter.GetBitmapSourceFromImageTable yönteminde bulunduğunu anlayabildim. Görüntü bir System.Windows.Media.Imaging.BitmapFrame'dan oluşturulduğunda, dönüştürücü bir anahtar kullanarak sözlüğünden birinde kaynak arayacaktır. Bu durumda, anahtar, BitmapFrame.Decoder.ToString() yöntemiyle döndürülen dizenin karma kodunu karşılık gelir. Ne yazık ki, resimlerim bir URI yerine bayt dizilerden oluşturulduğundan, kod çözücünün ToString yöntemi "görüntü" yi döndürür. Bu dize, görüntüden bağımsız olarak her zaman aynı karma kodu üreteceğinden, ImageSourceTypeConverter, tüm görüntülerin XPS belgesine zaten eklendiğini düşünecek ve kullanılacak ilk ve tek görüntünün URI'sini döndürecektir. Bu, ilk resmin neden çoğaltıldığını ve diğer tüm görüntülerin yerini aldığını açıklar.

soruna geçici çözüm için

Benim ilk girişimi Denemesi System.Windows.Media.Imaging.BitmapDecoder.ToString() yöntemini geçersiz oldu. Bunu yapmak için, BitmapFrame ve BitmapDecoder'u kendi BitmapFrame ve BitmapDecoder'a sarmayı denedim. Ne yazık ki, BitmapDecoder sınıfı tanımlayamadığım bir internal abstract yöntemini içerir. Kendi BitmapDecoder'ümü oluşturamadığım için, bu geçici çözümü uygulayamadım.

Geçici çözüm

önce mentionned BitmapSource bir BitmapFrame olduğunda, System.Windows.Xps.Serialization.ImageSourceTypeConverter.GetBitmapSourceFromImageTable yöntem bir sözlükte bir karma kodu arayacaktır. BitmapFrame değilse, görüntü ikili verilerine dayanarak bir CRC değeri oluşturur ve başka bir sözlükte arar. Benim durumumda

, ben BitmapSource böyle System.Windows.Media.Imaging.CachedBitmap olarak başka türde içine System.Windows.Media.ImageSourceConverter tarafından bayt dizilerden üretildi BitmapFrame sarmak karar verdi. Bu seçenekler sayesinde

var imageSource = new CachedBitmap(bitmapFrame, BitmapCreateOptions.None, BitmapCacheOption.None); 

, CachedBitmap çoğunlukla basit BitmapSource sarıcı: Gerçekten önbelleğe bitmap istedik vermedi beri, CachedBitmap will aşağıdaki seçenekleri yarattı. Benim durumumda, bu geçici sorun benim sorunumu çözdü.

İlgili konular