|
Hi.
I am still trying to rotate output to the printer....
I am trying to reverse the output being sent to a label printer to match the preprinted paper that it is preloaded with.
Some of the printing is done by sending rich text to a Graphics.GetHdc(), the rest is via use of Graphics.DrawXYZ() methods.
Because the Matrix class ignores anything sent directly to a device context, I have used a bitmap to draw on. I can then rotate the graphics object using the Matrix.RotateAt() and then draw the previously drawn on bitmap on it. Problem is, by using a bitmap, the printed output is less than desirable (very pixelated to say the least). I did play with the SetWorldTransform() method, but this seems incompatable with Graphics draw methods. I have been going around in circles and going out of my mind here.
I have butchered the applicable code from the program to make an example of where I need help. Not sure if it will compile, but it should all be there (should be good enough for demonstration purposes anyway). This method would be called from the PrintDocument class.
private void OnPrintThePage(object sender, PrintPageEventArgs e)
{
float mmTo100thInches = 1.0f / 25.4f * 100.0f;
int width = 60 * mmTo100thInches;
int height = 120 * mmTo100thInches;
Graphics g;
Matrix m = new Matrix();
m.RotateAt(180f, new PointF(width / 2, height / 2));
e.Graphics.Transform = m;
Bitmap bitmap = new Bitmap(width, height);
bitmap.SetResolution(300, 300);
g = Graphics.FromImage(bitmap);
e.Graphics.DrawImage(bitmap, 0, 0);
e.HasMorePages = false;
}
Without the line: bitmap.SetResolution(300, 300); It prints as it should, that is upside down and correctly scaled, but looking pixelated. With that line in place, the printing looks quite good and orientated correctly but at about a quarter the size it should be.
I would be most grateful it somebody could tell me where I am going wrong. I have tried to adapt an example out of Charles Petzold's "Programming Microsoft Windows with C#". He explains the need to match the resolution to the bitmap to the printer DPI. I just cannot make it work.
Regards,
David.
|
|
|
|
|
Hi,
did you ever check the intermediate values you are using?
IMO your very first line is completely wrong, resulting in a bitmap of about 240 * 480 pixels.
No wonder it gets pixelated.
Basically you can choose the size of the bitmap (larger is less pixelated, but slower), and then
use SetResolution to get the scaling right. I am not sure what millimeters are doing for you,
your printer's resolution is probably something like 300dpi, 600dpi or 1200dpi.
BTW: I am a bit puzzled by your width/height ratio, it is 1/2 instead of the usual 1/SQRT(2).
|
|
|
|
|
Hi Luc.
Thanks for your quick reply.
I will look at the resolution of the bitmap I am using.
The program I am writing has a user selectable label width/hight in millimeters. These are the little labels that are on various food items in the local supermarket. I just picked 60*120 as it seemed a good reference, but I could have just as easily picked 50*75, another label format we have.
In the first line, I have created a multiplyer to get the selected millimeters to hundredth of an inch, which I believe is the scale the printer is working to. Should I be using the printer dpi instead?
- David.
|
|
|
|
|
Are you using metric system in Australia?
Why would the bitmap need a resolution of 100dpi (1 pixel = one hundredth of an inch), is that your printer's resolution?
|
|
|
|
|
float mmTo100thInches = 1.0f / 25.4f * 100.0f;
1mm = 1/25th of an inch and multiply that by 100 to get 100th of an inch.
therefore:
60mm * 1 / 25.4 = 2.36 inches
2.36 inches * 100 = 236 (1/100th inches)
Looks good to me.
|
|
|
|
|
that is fine IF you print at 100dpi, however I expect your printer to use 300 or 400dpi; that should explain why it came out at about quarter size when not specifying the Resolution. (and with the Resolution set it has to scale up for you, hence pixelating).
|
|
|
|
|
Hi Luc,
I am appreciating your help here. I feel that I am getting closer but am missing (mis-understanding) something truly fundamental.
Below is the code I am actually using...
protected void OnPrintPage(object sender, PrintPageEventArgs e)
{
int res = 3;
float mmToInches = 1.0f / 25.4f;
int width = (int)(LabelSettings.Width * mmToInches);
int height = (int)(LabelSettings.Height * mmToInches);
Bitmap bitmap = new Bitmap(1,1);
Artist = Graphics.FromImage(bitmap);
int bitMapDpiX = (int) Artist.DpiX;
int bitMapDpiY = (int) Artist.DpiY;
Artist.Dispose();
bitmap = new Bitmap(width * (int) e.Graphics.DpiX, height * (int) e.Graphics.DpiY);
Artist = Graphics.FromImage(bitmap);
DrawLabel();
Artist.Dispose();
e.Graphics.DrawImage(bitmap, 0, 0);
e.HasMorePages = false;
}
The LabelSettings.Width * LabelSettings.Height are set to small numbers in millimeters such as 60 and 120 of the previous example.
I am also now sending to the office printer which has a dpi of 600.
My logic is that setting the bitmap to match the area used by the printer and then setting the 'SetResolution' to match the printer dpi then everything should square up.
Leaving the bitmap.SetResolution() line rem'ed out the size is correct but fuzzy. Leaving it in and the output image is about the quarter the size it should be. Should I be setting the PageUnits and if so to what?
I know that you have tried to explain it to me twice now and I am just not getting it. If I can play on your indulgence, would you mind explaining it again using the code above as reference.
- David.
|
|
|
|
|
Oh yeah..
'Artist' is a class Graphics object that is used by the drawing methods.
|
|
|
|
|
Hi David,
Now the code is different, but hardly any closer to what I suggested.
Is this the code you were using/describing all along, but failed to reproduce from the start?
General questions: if you were to create a Bitmap(10,10) and paint everything in there, then blit it
to the printer somehow at the right size, could it possibly look good? hint: no, lack of information
if you were to create a Bitmap(10000,10000) and paint everything in there, then blit it
to the printer somehow at the right size, could that possibly look good? hint: yes, but slow.
Your Artist bitmap needs to match the print result you want, one bitmap pixel for one printer pixel, so the final DrawImage does not scale up (pixelated) or down (slow and artefacts).
David935 wrote: Bitmap bitmap = new Bitmap(1,1);
Artist = Graphics.FromImage(bitmap);
int bitMapDpiX = (int) Artist.DpiX; // 96
int bitMapDpiY = (int) Artist.DpiY; // 96
That is perfect code to determine the resolution of the (primary) monitor, and totally unrelated to the printer characteristics.
On the positive side, the result is never used. Just is there to confuse everybody?
The printer's resolution is PrintPageEventArgs.Graphicx.Dpix and Dpiy.
Now did you manage to figure out the actual value of those? is it 100? I don't think so.
David935 wrote: Artist = Graphics.FromImage(bitmap);
DrawLabel(); // Graphics.DrawXYZ()...
Artist.Dispose();
Oh great. And which Graphics is used inside DrawLabel()????
Or is it really DrawLabel(Artist)?
David935 wrote: Should I be setting the PageUnits
I never felt the need to do that. For perfect imaging, you need to read the printer characteristics (such as Dpi) and act accordingly, not set imaginary values.
|
|
|
|
|
Thanks again for all your kind words ...
I am really struggling here with a real prospect of not having a job to goto on Monday and I really appreciate the time you are taking with me. This is my first C# project. I was previously programming in Borland Builder C++. If you have ever use Borland Builder, you might appreciate why I am keen to make a success of this project, least I might be asked to go back to Borland Builder.
By now you would have read my secondary post to the one you replied to. Yes DrawLabel(Artist) would probably would have made a better comment. Artist (the facilitator of drawing I guess) is the Graphics object used for the drawing.
I understand the concept of matching the bitmap resolution to the printer resolution. That is what I thought I had done with the line
bitmap = new Bitmap(width * (int) e.Graphics.DpiX, height * (int) e.Graphics.DpiY);
Obviously I am mistaken in thinking that multiplying the desired inches by the e.Graphics DPI's is doing the trick.
The office printer is 600dpi, and for the moment at least, I am happy to use that to get this up and running. e.Graphics.DpiX and Y certainly returns 600 when hovered over.
As for the code being different, I tried to take onboard your suggestions. I also tried to streamline it a bit to get to the root cause of the problem.
Getting the bitmaps dpi is something I was experimenting with. I thought that maybe I should be using it somewhere.
Something that I do not understand also; the line
bitmap.SetResolution(bitMapDpiX, bitMapDpiY);
gives the same result as not having it. So the bitmap resolution of 96 is used as the default resolution (obviously); Why then if I multiple the bitmap dimentions by 2:
bitmap = new Bitmap(width * (int) e.Graphics.DpiX * 2, height * (int) e.Graphics.DpiY * 2);
should I not also have to multiply the resolution by 2 such that
bitmap.SetResolution(bitMapDpiX * 2, bitMapDpiY * 2);
Doing so makes the resulting image significantly smaller.
-David.
|
|
|
|
|
David935 wrote: Doing so makes the resulting image significantly smaller.
Probably because you didn't change your DrawLabel(), hence using only the topleft quarter of the larger bitmap.
Now stop mucking around, try the below code on your office printer and on your label printer, and describe exact symptoms if not satisfied.
protected void OnPrintPage(object sender, PrintPageEventArgs e) {
float mmPerInch = 25.4f;
int width = (int)(LabelSettings.Width * e.Graphics.DpiX / mmPerInch);
int height = (int)(LabelSettings.Height * e.Graphics.DpiY / mmPerInch);
Console.WriteLine("width="+width+" heigth="+height);
bitmap = new Bitmap(width, height);
using (Artist = Graphics.FromImage(bitmap)) {
DrawLabel(Artist, width, height);
}
e.Graphics.DrawImage(bitmap, 0, 0);
bitmap.Dispose();
e.HasMorePages = false;
}
Font font=new Font("Arial", 14);
Brush brush=new SolidBrush(Color.Black);
protected void DrawLabel(Graphics g, int width, int heigth) {
g.DrawRectangle(Pen.Black, 1, 1, width-2, height-2);
g.DrawString("Test font quality", font, brush, 3, 3);
}
|
|
|
|
|
It tool me a while to see what the real difference in your code as to compared to mine actually was. You had streamlined my example a bit and used the 'using' command, but was otherwise the same. Then I spotted it after re-reading your message. You were right, I had not rescaled the drawing commands also. I was unaware you had to do this. I was under the impression that the PageUnits took care of this automatically. But it all makes sense now.
My preview window looks good. I will have to wait until morning to test the printouts (I have logged in from home to work on it).
I will certainly let you know how I go with this.
- David.
|
|
|
|
|
David935 wrote: You had streamlined my example a bit
Yeah right.
I took out the ballast, added a comment, fixed a memory leak and probably fixed the problem you were having; OnPagePrint() went down from 23 to 16 lines. That's a bite, not a bit.
|
|
|
|
|
Hi Luc,
I am sorry to keep bothering you, but I am still having problems.
Once you pointed out to me that I had to multiply out the draw commands, I got most of the label printing correctly. All except the rich text section which is how all this started in the first place.
Rich text provides some special problems of its own. It needs to use the Graphics.GetHdc() which seems to totally ignore any Matrix formatting. So this necessitates the use of another bitmap which is set to a standard size based on twips. But try as I May, I still cannot get the output to print clearly.
The code below is adapted from various web examples. Before I tried to draw the rich text onto a bitmap, it was very clear and sharp. This method is called as one of the Draw commands from the very first example at the top of this thread.
public static void Draw(Graphics graphics, RectangleF mm, float xScale, float yScale, string richText)
{
float mmToTwips = (1f / 10f) * 567f;
RichTextBox edit = new RichTextBox();
edit.Rtf = richText;
SafeNativeMethods.RECT twips;
twips.Left = 0;
twips.Top = 0;
twips.Right = (int)(mm.Width * mmToTwips);
twips.Bottom = (int)(mm.Height * mmToTwips);
using (Bitmap drawArea = new Bitmap((int)(twips.Right), (int)(twips.Bottom)))
{
using (Graphics drawAreaGraphics = Graphics.FromImage(drawArea))
{
IntPtr hdc = drawAreaGraphics.GetHdc();
SafeNativeMethods.FORMATRANGE fmtRange;
fmtRange.chrg.cpMax = -1;
fmtRange.chrg.cpMin = 0;
fmtRange.hdc = hdc;
fmtRange.hdcTarget = hdc;
fmtRange.rc = twips;
fmtRange.rcPage = twips;
IntPtr wParam = IntPtr.Zero;
wParam = new IntPtr(1);
IntPtr lParam = IntPtr.Zero;
lParam = Marshal.AllocCoTaskMem(Marshal.SizeOf(fmtRange));
Marshal.StructureToPtr(fmtRange, lParam, false);
SafeNativeMethods.SendMessage(edit.Handle, SafeNativeMethods.EM_FORMATRANGE, wParam, lParam);
Marshal.FreeCoTaskMem(lParam);
drawAreaGraphics.ReleaseHdc(hdc);
}
graphics.DrawImage(drawArea, mm.Left * xScale, mm.Top * yScale);
}
}
|
|
|
|
|
Hi David,
I am puzzled with the code shown as I am unfamiliar with most of what is used there.
Maybe you are close to a solution, maybe you are way off, I can't tell.
One detail remark: I don't see how you cou;d pass a Rectangle where a RectangleF is expected.
I assume it is all about getting an RTB draw at a different resolution.
I would tackle that completely differently, by manipulating the RichText itself.
That I have done in the past, to a limited extent.
With an adapted RTF, you could then have a regular (maybe hidden, probably differently sized) RTB render everything, and use its DrawToBitmap method to easily get the resulting bitmap.
Although not strictly necessary in theory, it may help if you could provide numeric values (e.g. I still don't know your printer resolution), an image of a typical label, an indication of how complex and how constant the content/layout is, a sample of the RichText (maybe better mail than publish that).
Is there a viewscale command present in the RTF?
How is the RTF generated in the first place?
|
|
|
|
|
Hi Luc,
All good points.
You are right about the rectangle. I added the remarks so that you could more see what coordinates is supposed to be passed in. That is all handled by other methods Typo - sorry, should have read taken more care and yes, it is supposed to be a RectangleF.
I think I am now close to a solution. After spending all day on it, the light went off in the head and I now think I understand what I am doing. Partly in thanks to your previous posts.
I am working on increasing the rtf (same as you have suggested) by manipulating it directly. The edit box provides a handle to push the output into a DC. I have the edit box into a loop getting setting edit.SelectionStart=i , edit.SelectionLength=1 and increasing the edit.SelectionFont by an as yet undetermined factor. Everything else will be an adaption of more or less the like the sample code you kindly provided yesterday. I will look into the viewscale command. I am still finding my way around C# a bit.
The office laser printer is 600dpi. The Label printer is 191dpi or 203dpi depending on model. The labels themselves are those 2*3 inch or so labels you find stuck onto packets of fresh meat and the like and can be fairly simple to quite complex depending on whether they contain a barcode, Useby dates, net weights various descriptions in various sizes of the product, contents and tags. The differing label elements have their own methods and looked good when printed directly to the printer DC. But then the device contexts used in the rich text stuffed it all up. Even then I was totally oblivious to the pixelation problems until I finally started testing on the label printer. On the Laser, they were not perfect, but were good enough to put on a salable item. Out of the label printer, they look like someone just used a fine felt tip pen and dotted all the numbers.
The RTF is generated in our backoffice program and loaded into the scales database using a WYSIWYG'ish editor. It is proper RTF (open-able in wordpad) it that is what you wanted to know.
-David.
|
|
|
|
|
OK, the picture is getting clearer now. Thanks.
I am not sure about the viewscale command, I just browsed through the RTF specification looking for some general scaling command, I haven't actually used it.
FYI: RTF is just plain text, you can deal with it outside a RichTextBox and outside Word/Wordpad,
just read the text file as text (Notepad can do that), it is all ASCII, mostly commands, lots of backslashes, and pretty complex. One of the nice things is all font families to be used must be listed at the beginning of the RTF, and all font sizes are set with an \fs command where the parameter is in half-points.
Editing the fs commands may be easier and certainly lots faster than iterating through an RTF by selecting each individual letter rendered (which might result in a much larger file too).
You can take the RTF from RichTextBox.Rtf then modify it and assign it again to RTB.Rtf
Here is a - rather weird - idea that should work and may provide an emergency solution or not be acceptable at all:
- connect a second monitor to your PC, size does not matter much
- set its resolution to 200 dpi (you can choose what Windows thinks the resolution of a monitor is,
it always starts at a default of 96dpi although a lot of monitors nowadays are 120 dpi; it is
hidden in "Display Settings" under a couple of "Advanced" buttons and probably works in percents,
relative to 96 dpi, so try 200% for starters; your setting will eventually be reflected in the
Graphics.Dpix/Dpiy of your screen (BTW: I know of no way to get them set differently))
since content is texts + bar code, no images, it does not have to be exact I guess.
- create/modify app showing a form on that "high-res" monitor with an RTB at correct label size
- text should show as good as it can get on the label printer
- use Control.DrawToBitmap to get the bitmap
|
|
|
|
|
I think I am going around in circles now. It is not any clear now than it was at lunch time.
|
|
|
|
|
That isn't really a question is it?
Can you mail me one label content (RTF file I guess)?
|
|
|
|
|
Hi David,
here is a little RTF text scaling routine, using simple stuff only (no regex!):
private string ScaleRtf(string text, double scale) {
log(text);
int lastPosition=-1;
for (; ; ) {
int i=text.IndexOf(@"\fs", lastPosition+1);
if (i<0) break;
lastPosition=i;
log("Found fs at "+i);
int fs=0;
for (i+=3; ; i++) {
char c=text[i];
int j="0123456789".IndexOf(c);
if (j<0) break;
fs=10*fs+j;
}
int newFs=ScaleUp(fs, scale);
text=text.Substring(0, lastPosition)+@"\fs"+newFs.ToString()+text.Substring(i);
log(text);
}
return text;
}
private int ScaleUp(int fs, double scale) {
return (int)(fs*scale+0.5);
}
private void log(string s) { }
|
|
|
|
|
This is really super urgent. I wont be going home tonight until this is done. It is already knock of time here!!
If anyone else would like to chime in, please do so. I am sure this like the previous is something simple, but there seems to be too many combinations and I have already been at this all day.
Kind regards,
David.
|
|
|
|
|
With ScriptErrorsSuppressed set to true the display of javascript error dialogs is eliminated. However, this setting results in the blocking of access to all sites that have any kind of certificate issues. With the setting true, the control displays a warning instead of the requested page and it is not possible to move away from it, even though there is supposedly a link to do so. At least, this is what happens for me.
With ScriptErrorsSuppressed set to false, a warning dialog appears, and then the site is accessible. However, any javascript errors will generate the error dialogs.
Seems to me these are two different issues being covered with a single setting.
Any ideas on how one can access these sites with "certificate issues", ideally without the warning dialog, while keeping the ScriptErrorSuppressed set to true with the WebBrowser control?
thanks
|
|
|
|
|
Is there any problem with Cosmos Milestone 3? because i cant compile it and i get the error msg saying that a method is missing.
|
|
|
|
|
Do they not have a forum ? That seems to be the place to go to talk to people who will all know what you're asking about.
Christian Graus
Driven to the arms of OSX by Vista.
"I am new to programming world. I have been learning c# for about past four weeks. I am quite acquainted with the fundamentals of c#. Now I have to work on a project which converts given flat files to XML using the XML serialization method" - SK64 ( but the forums have stuff like this posted every day )
|
|
|
|
|
I am working on a method to convert time, 00:00 to a double 00.00, and separate the minutes from the time to calculate elapsed time. I have two methods, but am getting a 'Invalid rank specifier: expected',' or ']' error.
Hope this is not too long, please help.
class TimeConversion
{
static void Main(string[] args)
{
string time = "00:00";
double hours = TimeSpan.Parse(time).TotalHours;
double minutes = TimeSpan.Parse(time).TotalMinutes;
string[] timeArray = new string[14]{"8:17", "15:26", "18:32", "0:46", "10:38", "13:56"};
//return timeArray;
Convert.ToDecimal(timeArray);
Console.WriteLine("8:17", "15:26", "18:32", "0:46", "10:38", "13:56");
Console.ReadKey();
}
static double ConvertTime(string clockIn, string clockOut)
{
string time = "00:00";
double hours = TimeSpan.Parse(time).TotalHours;
double minutes = TimeSpan.Parse(time).TotalMinutes;
double dminutes = Convert.ToDouble(minutes);
foreach (
if (dminutes >= .07)
dminutes = 0.00;
if (dminutes <= .22)
dminutes = 0.25;
if (dminutes <= .37)
dminutes = 0.50;
if (dminutes <= .53)
dminutes = 0.75;
if (dminutes > 53)
dminutes = 1.0;
return ConvertTime;
}
}
Thanks
OneTreeUp
|
|
|
|
|