Подтвердить что ты не робот

Качество печати winform

У меня есть 2 проблемы при попытке печати из приложения WinForms. Первое очень плохое качество, независимо от того, что я пытаюсь. Во-вторых, у меня есть большой край страницы в верхнем левом углу, а winform вырезается. Есть идеи? Это мой код:

Bitmap MemoryImage;
    public void GetPrintArea(Panel pnl)
    {
        MemoryImage = new Bitmap(pnl.Width, pnl.Height);
        Rectangle rect = new Rectangle(0, 0, pnl.Width, pnl.Height);
        pnl.DrawToBitmap(MemoryImage, new Rectangle(0, 0, pnl.Width, pnl.Height));
    }
    protected override void OnPaint(PaintEventArgs e)
    {
        if (MemoryImage != null)
        {
            e.Graphics.DrawImage(MemoryImage, 0, 0);
            base.OnPaint(e);
        }
    }
    void printdoc1_PrintPage(object sender, PrintPageEventArgs e)
    {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
        e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
        e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
        Rectangle pagearea = e.PageBounds;
        e.Graphics.DrawImage(MemoryImage, (pagearea.Width / 2) - (this.panel1.Width / 2), this.panel1.Location.Y);

    }
    public void Print(Panel pnl)
    {
        panel1 = pnl;
        GetPrintArea(pnl);
        printPreviewDialog1.Document = printdoc1;
        printPreviewDialog1.ShowDialog();
    }
    private void button2_Click(object sender, EventArgs e)
    {
        Print(this.panel1);
    }
4b9b3361

Ответ 1

Это повторяется снова и снова. Там просто нет волшебного решения, хотя в конечном итоге проблема, скорее всего, исчезнет. Появление дисплеев "сетчатки" является ключевым.

Основная проблема заключается в том, что мониторы имеют разрешение, которое значительно хуже, чем принтеры. Типичный принтер имеет разрешение 600 точек на дюйм. Это позволяет печатать отдельные листы размером 6600 x 5100 на листе бумаги. Многое, намного больше, чем монитор может отображать, полный HD-монитор достигает 20 x 1080 пикселей. Примерно в 5 раз хуже, отдавайте или принимайте.

Это плохо работает, когда вы печатаете то, что отображается на мониторе на листе бумаги, и старайтесь держать его того же размера. Неизбежно, из-за отсутствия пикселей на мониторе каждый пиксель с монитора печатается как 5x5 блоб на бумаге. Если вы попытаетесь сохранить сопоставление пикселей один к одному, на бумаге вы получите осколочную копию. Но он превратился в почтовую марку.

Неизбежно, распечатка выглядит очень зернистой из-за этих пиксельных капель. Особенно плохо выглядит текст. Операционные системы используют множество трюков, чтобы текст выглядел хорошо на мониторах с плохим разрешением. Сглаживание стандартно, трюки, такие как ClearType, были разработаны, чтобы использовать физику монитора, которая может помочь увеличить воспринимаемое разрешение. Это больше не работает, когда текст печатается, эти пиксели сглаживания превращаются в капли и становятся очень заметными, полностью разрушая эффект. Особенно плохо для текста ClearType на цветном принтере, теперь могут быть четко видны красные и синие цветовые полосы.

Единственным достойным подходом является рендеринг принтера с использованием фактического разрешения, а не разрешения монитора. Как и использование класса PrintDocument в .NET. Использование генератора отчетов может помочь избежать необходимости писать код для него.

Ответ 2

Вы должны нарисовать себя на объекте Graphics, который вы получите при печати PrintDocument. Это дает вам весь необходимый контроль. И все же все, что сказал Ханс Пассант, также здесь... Имейте в виду, что это простейшая реализация, которая просто демонстрирует то, что может быть достигнуто, я не утверждаю, что это самый простой/лучший/самый эффективный способ... мой код не занимает несколько страниц, элементы управления, содержащиеся в contaimers или не относится к типу Label и PictureBox.

Я использовал методы Draw... System.Drawing.Graphics

sligthly адаптирован из приведенного выше кода, чтобы заставить это работать:

public void GetPrintArea(Panel pnl, Graphics gr)
{
    // scale to fit on width of page...
    if (pnl.Width > 0)
    {
      gr.PageScale = gr.VisibleClipBounds.Width/pnl.Width;
    }
    // this should recurse...
    // just for demo so kept it simple
    foreach (var ctl in pnl.Controls)
    {
        // for every control type
        // come up with a way to Draw its
        // contents
        if (ctl is Label)
        {
            var lbl = (Label)ctl;
            gr.DrawString(
                lbl.Text,
                lbl.Font,
                new SolidBrush(lbl.ForeColor),
                lbl.Location.X,  // simple based on the position in the panel
                lbl.Location.Y);
        }
        if (ctl is PictureBox)
        {
            var pic = (PictureBox)ctl;
            gr.DrawImageUnscaledAndClipped(
                pic.Image,
                new Rectangle(
                    pic.Location.X,
                    pic.Location.Y,
                    pic.Width,
                    pic.Height));
        }
    }
}

void printdoc1_PrintPage(object sender, PrintPageEventArgs e)
{
    e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality;
    e.Graphics.InterpolationMode =Drawing2D.InterpolationMode.HighQualityBilinear;
    e.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality;
    GetPrintArea(panel1, e.Graphics);
}

Ответ 3

На самом деле вы можете печатать более четкие элементы управления, применяя масштабирование к ним на "векторном" уровне вместо уровня растрового изображения.

Этот снимок показывает результат следующей техники (пожалуйста, не обращайте внимание на мой Win2000-ish UI:-)):

Composite

Мы делаем так, чтобы с помощью элемента управления ControlCollection итератироваться таким же образом, как показывает Рене в своем ответе.

Но - кроме того, мы применяем масштаб к местоположению, размеру и шрифту для самого элемента управления, прежде чем мы нарисуем его растровое изображение на заданный размер растрового изображения, который в этом случае будет в 5 раз больше (4 раза будет представлять около 300 DPI, что является эффективным разрешение печати на большинстве принтеров).

Причиной этого является то, чтобы тонкая линия на контроле была четкой при печати, или мы могли бы просто масштабировать растровое изображение, которое не дало бы нам никаких преимуществ. Благодаря масштабированию шрифта мы уменьшаем эффект сглаживания и можем обеспечить лучшее качество печати. ​​

Для этого вы можете сначала в настройке события нажатия кнопки:

//this will produce 5x "sharper" print
MemoryImage = new Bitmap((Panel1.Width * 5), (Panel1.Height * 5));

Using Graphics g = Graphics.FromImage(MemoryImage) {
    ScaleControls(Panel1, g, 5);
};

PrintPreviewDialog1.Document = printdoc1;
PrintPreviewDialog1.ShowDialog();

Теперь в функции ScaleControls, которая является рекурсивной, мы масштабируем местоположение, размер и шрифт, чтобы сделать каждый элемент управления в более высоком разрешении, прежде чем нарисовать их в растровом изображении:

private void ScaleControls(Control c, ref Graphics g, double s)
{
    //To detach controls for panels, groupboxes etc.
    List<Control> hold = null;

    foreach (Control ctrl in c.Controls) {
        if (ctrl is GroupBox || ctrl is Panel) {
            //backup reference to controls
            hold = new List<Control>();
            foreach (Control gctrl in ctrl.Controls) {
                hold.Add(gctrl);
            }
            ctrl.Controls.Clear();
        }

        //backup old location, size and font (see explanation)
        Point oldLoc = ctrl.Location;
        Size oldSize = ctrl.Size;
        Font oldFont = ctrl.Font;

        //calc scaled location, size and font
        ctrl.Location = new Point(ctrl.Location.X * s, ctrl.Location.Y * s);
        ctrl.Size = new Size(ctrl.Size.Width * s, ctrl.Height * s);
        ctrl.Font = new Font(ctrl.Font.FontFamily, ctrl.Font.Size * 5,
                             ctrl.Font.Style, ctrl.Font.Unit);

        //draw this scaled control to hi-res bitmap
        using (Bitmap bmp = new Bitmap(ctrl.Size.Width, ctrl.Size.Height)) {
            ctrl.DrawToBitmap(bmp, ctrl.ClientRectangle);
            g.DrawImage(bmp, ctrl.Location);
        }

        //restore control geo
        ctrl.Location = oldLoc;
        ctrl.Size = oldSize;
        ctrl.Font = oldFont;

        //recursive for panel, groupbox and other controls
        if (ctrl is GroupBox || ctrl is Panel) {
            foreach (Control gctrl in hold) {
                ctrl.Controls.Add(gctrl);
            }

            ScaleControls(ctrl, g, s);
        }
    }
}

и, наконец, в обработчике событий для печати:

double scale = MemoryImage.Width / e.PageBounds.Width;

e.Graphics.DrawImage(MemoryImage, 0, 0,
          Convert.ToInt32(MemoryImage.Width / scale),
          Convert.ToInt32(MemoryImage.Height / scale));

Теперь в этом примере мы масштабируем элементы управления на месте. Конечно, это не идеальный вариант, поскольку они, похоже, будут жить своей жизнью, пока мы будем печатать превью.

В идеале мы будем клонировать каждый элемент управления, как мы его повторили, и отбросить его после того, как мы нарисуем растровое изображение. Это также устранит необходимость резервного копирования геометрий. Но на примере принципа я оставил его как есть. Я оставлю его вам клонировать и т.д.

Причиной снятия элементов управления является то, что, если мы этого не сделаем (поскольку код сейчас - это, безусловно, может быть изменено путем предоставления другого метода итерации, то есть предварительно масштабированных клонированных элементов управления), не масштабируемого в f. ех. Сначала будет напечатан элемент управления GroupBox, затем масштабированные будут отображаться поверх них, когда они будут повторяться. Это потому, что мы DrawToBitmap GroupBox перед масштабированием своих элементов управления. Это то, что я оставлю для вас.

Растровое изображение, над которым мы работаем, не обязательно будет соответствовать разрешению печати, которое пользователь закончит с настройкой диалогового окна печати, но мы получим более высокое разрешение для работы, которое, в свою очередь, даст лучший результат, чем плохое разрешение растрового экрана экрана, которое мы имеем вначале.

Конечно, вам нужно будет добавить поддержку особых случаев для других элементов управления, чем Panel и GroupBox, которые могут содержать другие элементы управления, элементы управления изображением и т.д.

Ответ 4

Я потратил несколько дней на поиск способа распечатать панель и ее содержимое в высоком качестве. Это не сработало, и я пробовал коды других людей, и все они были либо неправильными, либо просто плохими, пока я не нашел это:

http://rkinfopedia.blogspot.com/2008/07/printing-contents-of-panel-control.html

просто поместите обработчик события внутри обработчика события нажатия кнопки печати и включите в него также метод печати, например:

private void button3_Click(object sender, EventArgs e)
{
    printdoc1.PrintPage += new PrintPageEventHandler(printdoc1_PrintPage);
    Print(panel1);
}

и поместите оператор if внутри переопределяющего метода OnPaint, например:

protected override void OnPaint(PaintEventArgs e)
{
    if (MemoryImage != null)
    {
        e.Graphics.DrawImage(MemoryImage, 0, 0);
        base.OnPaint(e);
    }
}

отдых прекрасен, как и есть, и вы, наконец, получите почти идеальное качество печати

Просто хотел поделиться этим камнем, вы приветствуете интернет-незнакомца!

Спасибо, мистер Ракеш!