XPS 文件中打印的重复图像

2024-06-28

首先,我想指出,我已将此作为错误向 Microsoft 提出,但他们目前不愿意修复它。我正在寻找一种解决方法或更好的方法来实现我想要做的事情,因为我们的客户认为这是一个相当重要的问题。

The code

主窗口.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
}

很抱歉发布所有代码,但它涵盖了我看到问题的所有领域。如果这里有帮助的话微软错误报告 https://connect.microsoft.com/VisualStudio/feedback/details/1105434/writing-wpf-visual-to-xps-document-duplicates-images其中附有一个可以重现问题的示例项目。

问题
仅当写入 XPS 文件时才会出现此问题,其中仅打印第一个图像 3 次,如果单击“打印到设备”按钮,则会打印正确的图像。

我绑定到 byte[] 的原因是因为我将图像保存在本地 SQL CE 数据库中。我们将它们存储在数据库中,因为它们每个只有约 2KB 小,而且我们允许用户将自己的图标导入系统中使用,并且我们需要一种机制来保证它们不会被意外删除。

NOTE
我注意到,如果我没有如上所述绑定到 byte[],那么我就看不到这个问题。鉴于系统当前采用将图像存储在数据库中的方法,如果有解决方法,我宁愿坚持使用它,但我并不完全反对替换这些图像的存储机制。


我遇到过类似的问题,第一张图像被复制并替换了所有其他图像。就我而言,打印到设备、XPS 文档或 PDF 文档并不重要,问题仍然存在。

Analysis

我使用 .NET 程序集反编译器来弄清楚如何System.Windows.Xps.XpsDocumentWriter类处理图像以查明问题是在我的代码中还是在框架的代码中。我发现该框架使用字典将图像等资源导入到文档中。即使图像在 XPS 文档中仅导入一次,该文档也可以多次引用它们。

就我而言,我发现问题出在System.Windows.Xps.Serialization.ImageSourceTypeConverter.GetBitmapSourceFromImageTable方法。当图像是从System.Windows.Media.Imaging.BitmapFrame,转换器将使用键在其字典之一中查找资源。在这种情况下,键对应于返回的字符串的哈希码BitmapFrame.Decoder.ToString()方法。不幸的是,由于我的图像是从字节数组而不是 URI 构建的,因此解码器的ToString方法返回“图像”。由于无论图像如何,该字符串都将始终生成相同的哈希码,因此ImageSourceTypeConverter将认为所有图像都已添加到 XPS 文档中,并将返回要使用的第一个也是唯一一个图像的 Uri。这解释了为什么第一张图像被复制并替换所有其他图像。

Attempt

我解决该问题的第一次尝试是覆盖System.Windows.Media.Imaging.BitmapDecoder.ToString()方法。为了做到这一点,我尝试将BitmapFrame and BitmapDecoder进入我自己的BitmapFrame and BitmapDecoder。不幸的是,BitmapDecoder类包含一个internal abstract我无法定义方法。由于我无法创建自己的BitmapDecoder,我无法实施该解决方法。

解决方法

如前所述,System.Windows.Xps.Serialization.ImageSourceTypeConverter.GetBitmapSourceFromImageTable方法将在字典中查找哈希码,当BitmapSource is a BitmapFrame。当它不是一个BitmapFrame,它会根据图像二进制数据生成一个 CRC 值,并在另一个字典中查找它。

就我而言,我决定包装BitmapFrame由字节数组生成System.Windows.Media.ImageSourceConverter进入另一种类型BitmapSource例如System.Windows.Media.Imaging.CachedBitmap。由于我并不真正想要缓存位图,所以我创建了CachedBitmap将出现以下选项:

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

通过这些选项,CachedBitmap大多是一个简单的BitmapSource包装纸。就我而言,此解决方法解决了我的问题。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

XPS 文件中打印的重复图像 的相关文章

随机推荐

  • 使用张量流 tf-transform 进行数据标准化

    我正在使用 Tensorflow 对我自己的数据集进行神经网络预测 我做的第一个模型是与我计算机中的小数据集一起使用的模型 之后 我稍微更改了代码 以便使用具有更大数据集的 Google Cloud ML Engine 在 ML Engin
  • 使用 Auth0、withAuthenticationRequired 登录不会显示在 Gatsby 中

    我将 Gatsby 与 auth0 一起使用 当我用withAuthenticationRequired 然后我得到一个空白页 上面写着 重定向 import as React from react import withAuthentic
  • Python:pip 找不到 setup.py

    我怎样才能直接pip找到setup py My setup py文件位于 setuptools 3 5 1 I ran dustin dustin python setuptools 3 5 1 setup py egg info runn
  • 以编程方式添加超链接到列表项

    我想以编程方式获得以下 HTML ul li a href a li ul 我可以添加 li to ul But a to li 不可能 My code BulletedList ul new BulletedList ListItem l
  • React Table - useRowSelect 的单选输入

    如何在 React Table 中使用单选输入而不是复选框作为可选表 有一个复选框但没有单选按钮的示例 https github com tannerlinsley react table blob master examples row
  • 无法访问内存-gdb

    这是我的disas代码 Dump of assembler code for function main 0x00000000000006b0 lt 0 gt push rbp 0x00000000000006b1 lt 1 gt mov
  • Java进程的dump文件分析?

    如果我使用 Windbg 转储 Windows 上运行的 Java 进程 我可以 容易吗 分析 Java 堆 对象和线程吗 就像我可以使用 SOS 进行 Net 进程一样吗 否则 如何离线调试生产系统上发生的问题 Thanks Window
  • 等待异步TaskEx

    What is TaskEx In http www i programmer info programming c 1514 async await and the ui problem html start 1 http www i p
  • 类型错误:序列项 0:预期字符串,未找到 NoneType

    我正在努力改进战舰游戏 原始版本工作正常 没有错误 我编写了代码来帮助克服第一个版本每次都将船只放置在同一个位置的事实 因此我从一艘船 由两个方块组成 开始 我通过创建两个函数来完成此操作 第一个函数生成一个随机坐标 Destroyer 2
  • catch(...) 没有捕获异常,我的程序仍然崩溃

    我的测试仪遇到问题 我的应用程序在初始化时崩溃 我添加了更多的日志记录和异常处理 但它仍然崩溃并显示通用的 此程序已停止工作 消息 而不是触发我的错误处理 鉴于我的 main 看起来像这样并且有catch 什么情况下不会触发 try sim
  • Collectors.groupingBy() 返回按升序排序的结果 java

    我按降序发送结果 但得到的输出按升序排列 List
  • 如何使用RxJsdistinctUntilChanged?

    我正在开始使用 RxJs 使用 v5 beta 但不知何故我不知道如何使用distinctUntilChanged 如果我在 babel node 中运行下面的代码 其输出是 a 1 key a state 1 Next value 42
  • Boto 与 EC2 IAM 角色间歇性“无法加载凭证”

    我使用 Elastic Beanstalk 环境来部署 Web 应用程序 并为应用程序将在其上运行的实例设置了 IAM 角色 99 99 的时间里一切都完美无缺 但是我会间歇性地在日志中看到错误 请求失败显示 botocore 错误 如下所
  • Python - 比较2个文件并输出差异

    我的目标是编写一个脚本来比较文件中的每一行 并根据此比较创建一个新文件 其中包含第二个文件中不存在的文本行 例如 File 1 Bob 20 Dan 50 Brad 34 Emma 32 Anne 43 File 2 Dan 50 Emma
  • SQL Server 2008:TOP 10 和不同的一起

    正如标题所示 我正在使用 SQL Server 2008 如果这个问题非常基本 我深表歉意 我才使用 SQL 几天 现在我有以下查询 SELECT TOP 10 p id pl nm pl val pl txt val from dm la
  • excel vba范围单元格错误对象定义[重复]

    这个问题在这里已经有答案了 我一直在 Excel 中开发一个宏 该宏对一张工作表 次要 中的表格进行排序 当满足条件时 它应该将该表中的数据添加到第二张工作表 Sheet1 中的另一个表格中 但是我一直运行时出现 1004 错误 对象未定义
  • 需要更好的等待解决方案

    最近 我一直在用 C 编写一个程序 它可以 ping 三个不同的网站 然后根据通过或失败的情况 它将等待 5 分钟或 30 秒 然后再次尝试 目前我一直在使用ctime库和以下函数来处理我的等待 然而 根据我的 CPU 计量表 这是一个不可
  • Jetpack Compose Navigation - 将参数传递给 startDestination

    我正在构建的应用程序使用带有路线的组合导航 挑战在于起始目的地是动态的 这是一个最小的例子 class MainActivity ComponentActivity override fun onCreate savedInstanceSt
  • Stream API - 如果紧随其后放置 filter(),sorted() 操作如何工作?

    采取以下代码 对列表进行排序 然后对其进行过滤 public static void main String args List
  • XPS 文件中打印的重复图像

    首先 我想指出 我已将此作为错误向 Microsoft 提出 但他们目前不愿意修复它 我正在寻找一种解决方法或更好的方法来实现我想要做的事情 因为我们的客户认为这是一个相当重要的问题 The code 主窗口 xaml