dymoprint
dymoprint copied to clipboard
420p label offset
The labels are printed offset to the bottom (when reading the text) of the label.
Attached is a photo of the result (tested with release v2.2.1 on Linux):
- a 12mm tape (black on white), with four prints in increasing tape-width specification.
- a 6mm tape (black on yellow) with the same four prints. My 19mm tape ran out before I could test and is on order now, but I suspect that aligns correctly.
A possibility could be that the position of the tape is wrongly referenced:
- The cartridges all sit in the cartridge-holder, with the cartridge edge facing the top of the text being the fixed reference surface for the cartridge. This is the unlabeled face of the cartridge, with the labeled side facing the door of the printspace.
- However, the 6mm and 12mm (and presumably 9mm) cartridges are the same height. -- The 6mm tape sits centered in the oversized housing, with spacers above and below. -- The 12mm tape takes up the whole height of the cartridge. -- The printhead thus sticks out on the side of the cartridge with the label.
- The 19mm tape housing is higher, but again starts from this top reference surface.
Also attached is a schematic (apologies to your eyes)
(I lack any skill in printer driver development...)
It is very strange because it uses the same cartridge models as PnP (D1), so I assume they used the same printing header. Why would it work differently? Have you cross checked with official software or .. well, official method of use?
However, if this is shown to be a consistent behavior on this model of label printers then I guess it could be a matter of adding an offset for this particular model.
The unit has a little on-device keyboard and that works fine, the mode-switch-to-storage-enclosed "dymo mini" (or something like that) windows software also prints perfectly aligned. The official windows app from their site didn't see my labelprinter. I looked at youtube video's of the PnP with D1 cartridges, and they look to be printing in the same orientation - so I'm at an utter loss.
How would I go about experimenting with offsets? I don't have the first clue about driver development.
I can confirm the behavior for my 420p. Using the official windows app, it prints correctly. Using dymoprint under arch linux, I have the same offsets as described by @Mousketeer . I'd be happy to help or try things out. But, same over here: It would be great to get some (even rough) directions where to start. @tomek-szczesny , can you help here?
@khrise I'm afraid we know pretty much the same about this code, as I'm not a contributor. Just someone who likes their PnP and tests new features. :) But fear not, this is not a driver per se, just a program that creates USB packets and sends them. I think that a portion of the code that differentiates between various models of Dymo printers must be found, and add a line that overrides offset value if it exists. If not, such value must be added.
My understanding of Python is very limited, I can read it mostly but not contribute, unfortunately.
As luck would have it my new 19mm tape came in this week, here are some tests:
A QR, large (5000px) jpg, and text at size 19 (dymoprint -t 19 -qr "contents" -p image.jpg) on 19mm tape. The top of the lowercase letters is just about in the centre of the tape.
And, again on 19mm tape, text with the different sizes (-t NN).
How the 12mm tape (green) is seated in the machine compared to the 19mm:
And this is also interesting (coincidence?):
So various clues:
- The 19mm tape print is not centered, but too far to the bottom (side of cartridge with identification label)
- The print offset is in the wrong direction
As for the material properties of the tapes:
- The 19 and 12mm tapes are aligned at the top of the cartridge
- The top of the 6mm tape sits at approx 3mm from the top of the cartridge.
- 9mm still unknown
An older version of the full windows software suite works perfectly.
Any hints on where to start digging would be greatly appreciated :-)
Excellent work! If you're able to calculate necessary offsets for each tape size, that would be half job done. :) The code suggests the print resolution is 8 dots per mm, and offsets must be added in dots, not mm.
D1 series cartridges (that fits PnP for example) appear to be centered regardless of tape size, which confirms your findings. Here's a photo of 9mm and 12mm I have.
So clearly a table must be added, with offsets per each printer model and tape size. I suggest doing it in such a way that if a printer is not present in table, 0 offset is assumed, for maximum compatibility.
I think this is the place to start: https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/dymo_print_engines.py#L395C34-L395C34
Here, label_matrix
is created, which I assume contains raw print data.
Then, dymo_devs
is created and populated, which contains a list of connected printers. At some point one printer is chosen, and by reading this variable we may figure out which printer is in use.
Now, just before the printing is about to begin, here: https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/dymo_print_engines.py#L508 the label_matrix
can be manipulated to implement offset.
I'm not familiar with good Python practices, but knowing other projects, a table with offsets per each printer and tape width should be stored in a separate constants file. For example here: https://github.com/computerlyrik/dymoprint/blob/master/src/dymoprint/constants.py
I can offer a check whether your changes did not break the PnP compatibility. :)
Now when I think about it, this will not solve the problem, sorry. Like I said, I can only speculate because I don't know anything more than any of you.
label_matrix
contains only the data to be printed, so it is only as wide as the selected tape size. The actual USB data sending happens in this file:
https://github.com/computerlyrik/dymoprint/blob/master/src/dymoprint/labeler.py#L205
If someone completely understands this file, I guess the problem will be solved quickly.
I speculate that it's the PnP printer itself that centers received data in the printing area. If print rows are shorter than maximum, the printer may choose to print on its header center, which made sense in older models (I suppose D1 was the first tape cassette type they had).
Looking at four test prints on 19mm tape I see that 420p behaves differently, aligning the print area to the bottom. This has to be compensated for, because the code assumes automatic centering.
I fear that in order to print on your printers correctly, the printer must always think the tape is 19mm wide, and label_matrix
have extra blank rows (or columns) to move the print area properly.
Mmh, ok, I'm getting somewhere. If I change the y-part of the position to 0 here:
https://vscode.dev/github/computerlyrik/dymoprint/blob/master/src/dymoprint/dymo_print_engines.py#L299
I get the following result. The position of the text appears to be correct, but the upper part of the text is cropped.
(Printing on 9mm tape)
If you run dymoprint --preview
, does what you see on the screen correspond at all to what's being printed?
If dymoprint --preview
appears correct, then probably the issue is how the data is being encoded and sent to the printer. It could be that whatever scheme works for encoding up to 12mm breaks down at 19mm and requires some adjustment.
Good point - tried the preview at the very moment. And nope: the preview is also "broken" in a similar way.
So the positioning appears to be correct, but the image does not contain the whole text.
The vscode links don't work on my desktop, just saying.
Yup, that's exactly why I later said my first comment isn't the best idea.
I think what should be done is to add more columns to label_matrix
, to match (19 * 8) matrix width, and make this call:
https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/dymo_print_engines.py#L506C1-L506C79
always with 19mm tape width.
The trick is to add correct amount of blank columns on front and back of existing matrix for each tape size.
It seems like it works for me. Could the cutoff be a result of the adjustment you made?
$ dymoprint --version
dymoprint 2.2.1
$ dymoprint --preview -t 19 test
Demo mode: showing label..
████████████████████████████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
█████████████████████████████████████████████████████████████▀ ██████████████████████████████
█████████████████████████████████████████████████████████████ ▀█████████████████████████████
█████████████████████████████████████████████████████████████ █████████████████████████████
███████████████████████████████▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀████████████████████████
████████████████████████████▀ ▀▀██████████████
███████████████████████████ ██████████████
██████████████████████████ ██████████████
██████████████████████████ ▄▄███████████████████████████ █████████████████████████████
█████████████████████████▄ ▄█████████████████████████████ █████████████████████████████
██████████████████████████ ██████████████████████████████ █████████████████████████████
██████████████████████████ █████████████████████████████ █████████████████████████████
███████████████████████████ ████████████████████████████ █████████████████████████████
████████████████████████████ ▄▄█████████████████████████████▄▄▄▄▄▄█████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
██████████████████████████████████████▀▀▀▀ ▀▀▀█████████████████████████████████████████
██████████████████████████████████▀▀ ▀█████████████████████████████████████
███████████████████████████████▀▀ ▀██████████████████████████████████
█████████████████████████████▀ ▄ ▀████████████████████████████████
████████████████████████████▀ ▄▄▄██████ ███▄▄ ▀███████████████████████████████
███████████████████████████▀ ▄██████████ ███████▄ ██████████████████████████████
██████████████████████████▀ ▄████████████ █████████ ▀█████████████████████████████
██████████████████████████ ██████████████ ██████████ █████████████████████████████
██████████████████████████ ██████████████ ██████████▄ █████████████████████████████
█████████████████████████▀ ███████████████ ███████████ ▀████████████████████████████
█████████████████████████▄ ███████████████ ███████████ █████████████████████████████
██████████████████████████ ▀██████████████ ██████████ █████████████████████████████
██████████████████████████ ██████████████ █████████▀ █████████████████████████████
██████████████████████████▄ ▀█████████████ ███████▀ ██████████████████████████████
███████████████████████████ ▀████████████ ████▀▀ ███████████████████████████████
████████████████████████████ ▀███████████ ▄████████████████████████████████
█████████████████████████████ ███████████ ▄▄██████████████████████████████████
██████████████████████████████▄ ▄▄████████████ ▄▄▄█████████████████████████████████████
███████████████████████████████████████████████▄▄▄▄▄████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
██████████████████████████████▀▀████████████████████████████████████████████████████████████████
█████████████████████████████ ▀▀███████████████▀▀ ▀▀███████████████████████████████████
████████████████████████████ █████████████▀ ▀████████████████████████████████
███████████████████████████ ▄█████████████ ███████████████████████████████
██████████████████████████ ▄█████████████ ▄▄▄▄ ██████████████████████████████
██████████████████████████ █████████████ ▄███████ █████████████████████████████
█████████████████████████▀ █████████████▀ ██████████ █████████████████████████████
█████████████████████████▄ █████████████ ███████████▄ ▀████████████████████████████
██████████████████████████ ███████████ ▄████████████ █████████████████████████████
██████████████████████████ ▀█████████ ████████████ █████████████████████████████
███████████████████████████ ▀▀████▀ ████████████▀ █████████████████████████████
███████████████████████████▄ ▄███████████▀ ██████████████████████████████
█████████████████████████████▄ █████████████ ███████████████████████████████
███████████████████████████████▄▄ ▄▄███████████████▄ ▄████████████████████████████████
████████████████████████████████████▄▄▄▄▄███████████████████████████████████████████████████████
██████████████████████████████████████████████████████████████▀▀▀▀██████████████████████████████
█████████████████████████████████████████████████████████████ ██████████████████████████████
█████████████████████████████████████████████████████████████ █████████████████████████████
█████████████████████████████████████████████████████████████ █████████████████████████████
█████████████████████████████▀▀ ▀▀▀▀▀▀▀▀████████████████
███████████████████████████▀ ██████████████
██████████████████████████▀ ██████████████
██████████████████████████ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄██████████████
█████████████████████████▀ █████████████████████████████ █████████████████████████████
██████████████████████████ ██████████████████████████████ █████████████████████████████
██████████████████████████ ▀█████████████████████████████ █████████████████████████████
██████████████████████████▄ ▀████████████████████████████ █████████████████████████████
███████████████████████████▄ ▄████████████████████████████ █████████████████████████████
████████████████████████████▄▄██████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████████████████████████████
Yes, definitly. Using the code from the repo, I get the same (correct) preview. However, I changed the y-offset in dymo_print_engines.py#L299 to 0, just to find out where I can adjust the erroneous offset in the printed labels (the original issue). With this change, the text on the label gets printed as shown above. And also the preview shows that the image already contains only half of the text. So, the image doesn't get rendered correctly, now we have to find how to fix that, in order to implement "switches" for the 420p.
I assume it's either somewhere in render_engine.render_text
or render_engine.merge_render
But, as @tomek-szczesny says, I might as well be on the wrong track.
Taking a quick look, I'm quite skeptical of this line:
https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/labeler.py#L33
I'd try adding import math
and replace int(...)
by math.ceil(...)
@khrise, I'll try to put everything I know now in simpler terms:
Let's call label_matrix
our "canvas", which is of a size of a label, and what the CLI preview shows.
What you edited is a routine that plots text on a canvas, thus does not affect pictures, barcodes, QR and so on.
I would say that our canvas and its contents are completely fine.
As far as I can see, the canvas is sent to the printer verbatim with no changes. This means it has variable width.
The problem is how the printer interprets canvas smaller than the printer head - which in your case is 19mm, and in PnP case it's 12mm.
I made a similar test to yours T19 prints which I find the most valuable. This is a 12mm tape.
It appears both printer models behave the same, as indicated in the programming manual lined in the labeler.py
file header:
https://download.dymo.com/dymo/technical-data-sheets/LW%20450%20Series%20Technical%20Reference.pdf
This is a programming manual of a completely different Dymo product, so things may or may not work.
Here on page 15 we can read that the printer is made aware how many bytes per line will be sent using "Set Bytes Per Line" command, and that the right side of the label will be left blank. This is in fact what we observe, the printer prints the canvas on the leftmost edge of the label. This is wrong, and this is probably wrong in all printers supported by dymoprint today.
I'll share another picture where I use a lot of 9mm labels. The one on top has been printed using a handheld Dymo 160, and those on the lower end using PnP and dymoprint. The text is too low. If I used 6mm labels, I would probably notice this before.
The solution:
On the same page of dymo documentation, you can see a "Set Dot Tab" command, which is supposed to shift the whole print away from the leftmost edge.
This command is sent to the printer here, before it starts printing:
https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/labeler.py#L219
You could try experimenting with adding +20 to dottab
variable one line before and see if it makes any difference.
If yes, then all we need is a dottab table for each printer and each label size, instead of whatever method is in use right now.
If not, then we know this function simply doesn't work and may be removed. After all, this is a documentation of a completely different Dymo product and not everything must be implemented in our label printers.
If it doesn't work, we'll have to achieve the same result by increasing canvas size, thus adding more "white space" under the existing canvas. This is what I called "adding columns to label_matrix
".
And the winner is... @maresb ! :) At least, he pointed me to the relevant code. int
as compared to math.ceil
doesn't make so big a difference, in that case. However, I experimented a little and found out that return int(16 * tape_size / 12)
in max_bytes_per_line
appears to do the trick. I still need to verify that this works with other tape widths, too. I tried with 12mm an it worked like a charm.
@tomek-szczesny : Increasing dotTab makes the problem even bigger, moving the text further down on the label.
Well, it's not that simple, apparently. I will figure out appropriate max_byte_per_line
s for other type widths, and get back to you here.
I think this may take you a long time to figure out a proper setup for all tape widths and all supported printers, if you wish to do it by guessing which variable should be tweaked.
I'm glad to know dottab manipulation works. Have you tried reducing it then? Maybe it's too big? Just don't make it negative.
Hey, cool! Thanks a lot for all the experimentation @khrise! I don't think there are so many tape sizes, so maybe we just replace that formula with a lookup dictionary.
@maresb , that's what I think, too. A lookup table per device with sane defaults (or fallback to the existing method). I'm not too familiar with python development, so would you be able to prepare something, or give me a few hints on how you would implement a device specific config? If not, I'd be able to come up with "something". :)
@tomek-szczesny , dotTab manipulation doesn't get us anywhere. The effective dotTab varies between 0 and 5 or so. Like I said, increasing it makes the situation worse, and we can't set it below 0, which we would need to get the text further "up".
dottab manipulation does work, and it represents 8 dots, or ~1mm. Just like the documentation said.
In my case it moves the text up like it should, no idea why it works differently in your code. Perhaps you didn't undo some of your previous hacks.
I removed the matrix optimization routine just to be sure I'm working with real dottab values. It's completely unnecessay on such small and slow printers anyway.
https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/labeler.py#L210-L215
Since dottab cannot work with fractions it's not worth trying to solve the problem this way - we could end up with 0,5mm offset in the worst case.
The solution - and I'm completely sure of this - is to expand canvas appropriately, after it's fully populated.
However, I experimented a little and found out that
return int(16 * tape_size / 12)
inmax_bytes_per_line
appears to do the trick.
Because it artificially increases the label size in uncontrolled way. https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/dymo_print_engines.py#L166
I mean, yeah it works for one printer and one tape size, but it breaks the math in the whole program. "bytes per line" loses its meaning, and builds up such a mess I swear I'll fork this project if you pull a hack like that.
The best solution, that will always work per given printer head size, is to expand canvas up to the maximum tape size for the printer - by adding the right amount of empty lines on the bottom and top. The bottom side lines will move the content up, and the top lines must be added to round the dot number to full bytes. Since the 19mm tape reportedly is not centered like other sizes are, this cannot be done algebraically and LUT will be necessary. Probably the best idea is to build a LUT for each printer model, before someone else shows up with yet another Dymo and misaligned prints.
But given that my credibility in this thread has been thrown to trash and some of you want to guess how to proceed, despite asking for advice beforehand... go ahead, I won't spoil the fun. Good luck!
But given that my credibility in this thread has been thrown to trash and some of you want to guess how to proceed, despite asking for advice beforehand... go ahead, I won't spoil the fun. Good luck!
@tomek-szczesny, I greatly value your insights and perspective, but please let's not have this tone. It is not constructive and is frankly quite offensive.
And the winner is...
Also @khrise this is a collaboration and not a contest. I'm glad what I wrote was useful, but I think @tomek-szczesny has a fairly solid understanding here, better than mine.
Back to the subject matter:
- Let's get rid of
dottab
and line trimming. It adds complexity and doesn't seem helpful. - Does anyone know exactly how many pixels tall the 420p can print? Or have some idea about how to figure this out?
Didn't mean to sound aggressive at all, just wanted to get the message across, that either we seek the best working and maintainable solution the meritocratic way, or.. that other approach. I'm glad we agree.
I think the dottab
related trimming can stay, it doesn't seem to do any harm. I saw no change in performance after removing it. And it may become necessary again if dymoprint
start supporting much larger printers. Just wanted to point out it's not currently necessary.
To test the 420p maximum label width in pixels I can't think of any other way than experimenting. I created a pattern that can be used for testing that. A high resolution photo of the portion that didn't fit will unambiguously tell us how many pixels did fit on a print. In order to print it I would temporarily modify software to think that label is 20mm wide and doesn't do any image scaling whatsoever. I think Dymo doesn't crash when it receives data that doesn't fit on the print.
Officially it has 180dpi resolution, which means 7,087 dots per mm. So it is confirmed: https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/constants.py#L72 This could be true for all Dymos, I'm used to Japanese 8 dots per mm printers (203dpi). Unfortunately I've never seen a thermal printer that can report supported pixel width.
Now, onto the actual offset problem:
I suggest adding some code to print_label
method:
https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/dymo_print_engines.py#L395
Now ideally we would like to modify the picture before it gets converted into a byte array for dymo printer, but at the same time we have to know beforehand what printer and label size are we dealing with.
So I think we would have to move things around a bit.
The code generating label_matrix
should be moved after the printer is detected, so right before the dymo labeler object is created:
https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/dymo_print_engines.py#L408-L423
https://github.com/computerlyrik/dymoprint/blob/2476b1722b0fbede7720be0d554e591c672fc54d/src/dymoprint/dymo_print_engines.py#L505-L506
Then, we will create a bigger picture and paste the original print_label
onto it, thus creating an offset.
This will fit nicely right at the beginning of print_label
generating code, mentioned above.
It is worth noting the picture size must be rounded to 8 pixels, so it may be packed into bytes afterwards.
The pseudocode would go like this:
# convert the image to the proper matrix for the dymo labeler object
offset = lookup_table[printer_model, tape_size]
offset_rem = 8 - (offset % 8)
label = Image.new("1", (label_bitmap.width, label_bitmap.height + offset + offset_rem), )
label.paste(label_bitmap, (0, offset_rem))
label_rotated = label.transpose(Image.ROTATE_270)
labelstream = label_rotated.tobytes()
(...)
Didn't mean to sound aggressive at all
Phew, that didn't work well, and I still need to recover a little, to be honest.
Anyway, apart from all my hacking and guessing, I tried some of your suggestions, with some success. First of all, I managed to print your image to see how many pixels the p420 can print. I disabled image scaling and printed the picture "raw". (Btw: the command line always requires a text, it won't allow printing an image only, is that intended?). However, I had to rotate your image by 180 degrees, because the got printed top-aligned, (otherwise the 140 was printed, but the 40 and the 20 was not - this might explain our dtop-moves-text-up-or-down discrepancies above). That's the result.
Does that look plausible?
I think Dymo doesn't crash when it receives data that doesn't fit on the print.
It doesn't. However, I had to disable the check in the code.
Then, we will create a bigger picture and paste the original print_label onto it, thus creating an offset.
This looks good and achieves good results. Only tested for 12mm tape until now. Using your pseude code from above, I found an offset of 4 to achieve good results for a 12mm tape.
Oh no, I need to print that on a 19mm tape, right?
Yup, we wanted to see the 19mm tape. Although I'm not entirely sure what we need that dot number for, @maresb?
You also need to fool the code that 19mm tape is in fact bigger, like 21mm. On your test print on 12mm tape there are visible borders on both sides which are not caused by hardware, we'd like to avoid that for this test. Anyway the pattern appears to work nicely, I see 79 dots across, which is 11.15mm. Which is weird, why not 80? I'll do the same pattern on my printer later.
Indeed, we'll have to take into account that your printer may have different firmware and stuff. Hopefully a look up table of offsets for each possible mode of operation will solve this problem once and for all, and for all of us. :) I'll buy a 6mm tape for PnP and find the optimal offset for this model as well.
The interesting side effect we will achieve here is that 9mm and 6mm prints will be essentially without any margins. :)
(Btw: the command line always requires a text, it won't allow printing an image only, is that intended?).
That should become another issue, in my opinion, feel free to create it. I suspect this was the first functionality implemented, thus used to be mandatory.
I feel like there's something very fundamental I'm not understanding here. I've tried rereading this thread, but I still don't get it...
On one hand it seems like we're passing an image of the incorrect height which is causing the label to not be centered. So it seems like we should simply use a canvas of the correct size, no? Like do a LUT from label width to canvas height. Then what is all this talk about offsets, and making things dependent on device?
I also counted 79 dots and was slightly confused. Maybe the topmost and the bottommost pixels are not reliably printable?
I agree that image-only should be supported, and should be opened in a new issue.
Ah, the point is that the canvas size for a given machine is always the same, but when printing on a smaller-sized label we need to map onto a subset of the canvas according to how the label is physically positioned?