views:

287

answers:

1

I need to draw a line one inch long on any device given a Graphics reference to it. I need it to be an inch long regardless of what Transform is set to. Let's assume that the scaling factor of the transform is given by scale in both horizontal and vertical directions.

Some C++/CLI code:

g->DrawLine(Pens::Black, 50.0f, 50.0f, 50.0f + oneInchEquivalent / scale, 50.0f);

Now that was not difficult at all! Now all we need to do is calculate oneInchEquivalent.

g->DpiX gives me a distance of what looks like one inch on screen but not on the printer. It seems that on printers, drawing a line of 100 units with g->PageUnit set to GraphicsUnit::Display will give me a line one inch long. But, I really need this to work regardless of the PageUnit setting. In fact, changing PageUnit will change the width of the pen!!

Edit: I have tentatively accepted the only answer here as it's pretty close to what I am looking for.

+8  A: 

The answer became rather long after a couple of edits, so here is the final result:

Setting the PageUnit property of the Graphics object to GraphicsUnit.Pixel and taking in multiplying coordinates with the DpiX and DpiY values will render the expected result on both display and printer devices.

private static void DrawInchLine(Graphics g, Color color, Point start, Point end)
{
    GraphicsUnit originalUnit = g.PageUnit;
    g.PageUnit = GraphicsUnit.Pixel;
    using (Pen pen = new Pen(color, 1))
    {
        g.DrawLine(pen,
            start.X * g.DpiX,
            start.Y * g.DpiY,
            end.X * g.DpiX,
            end.Y * g.DpiY);
    }
    g.PageUnit = originalUnit;
}

You can have it paint on a Form (or some control):

using (Graphics g = this.CreateGraphics())
{
    Point start = new Point(1, 1);
    Point end = new Point(2, 1);
    DrawInchLine(g, Color.Black, start, end);
}

...or send the output to a printer:

PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
    PrintDocument pd = new PrintDocument();
    pd.PrinterSettings = dialog.PrinterSettings;
    pd.PrintPage += (psender, pe) =>
    {
        Point start = new Point(1, 1);
        Point end = new Point(2, 1);
        DrawInchLine(pe.Graphics, Color.Black, start, end);
        pe.HasMorePages = false;
    };

    pd.Print();
}

This does, however, rely on setting the PageUnit.

Fredrik Mörk
You can try CutePDF for a printer emulator. You can see the differences there.
Vulcan Eager
I just realized I have a printer standing here; tried to print on it; came out at exactly one inch. Will update with the code.
Fredrik Mörk
Thanks Fredrik, but I need to do this using Math alone, without changing the PageUnit setting. Changing PageUnit will affect the width of the pen. See the last part of my question.
Vulcan Eager
Ah, missed that part... Unfortunately I cannot see a simple solution, apart from using `Pixel` as base unit for all drawing and scaling it to the target device using `DpiX` and `DpiY`.
Fredrik Mörk
I'll accept this anyway. It's pretty informative.
Vulcan Eager