DrawTextEx
- Posted by Jonas Temple <jktemple at yhti.net> Mar 21, 2001
- 556 views
All... I recently posted a message about examples of doing printing in Windows and anyone who had routines to handle word wrapping with printing. From what I gathered, most of us are not doing much printing in Windows with Eu. To that end I have implemented Windows' DrawTextEx function into my Win32Lib. Here's what you give the routine: - control id (in this case, Printer but you could try other controls) - text to draw - length of text - a sequence of four atoms defining the left, top, right and bottom boundaries of your rectangle on the page where you want to print a block of text - formatting options (too many to mention, check out http://msdn.microsoft.com/library/psdk/gdi/fontext_4pbs.htm for a compelete description of the function/values) - a structure for additional formatting options (I haven't implemented this one, just pass 0. Please, if you need this structure and implement, pass it on!) The function returns a sequence containing: - Actual height of text (value returned by Windows) (atom) - Actual string printed (can be different than what is passed depending on the formatting options) (sequence) - Actual left boundary of rectangle (atom) - Actual top boundary of rectangle (atom) - Actual right boundary of rectangle (atom) - Actual bottom boundary of rectange (atom) Use DT_WORDBREAK to specify that you want to wrap lines down the page with word break (yes, Windows does this FOR you! Can you believe it?) The piece that I like about this function is the DT_CALCRECT formatting option. When you specify this option the routine doesn't actually print the text but returns the actual dimensions needed to completely print the text. That way you can check if the text exceeds the length of the page. Here's what I added to Win32Lib: I added these lines AFTER the xDrawText function in Win32Lib: -- Beg CJT01 xDrawTextEx = registerw32Function(user32, "DrawTextExA", {C_POINTER, C_POINTER, C_INT, C_POINTER, C_UINT, C_POINTER}, C_INT), -- End CJT01 I added these lines AFTER the definition of DT_VCENTER in Win32Lib: -- Beg CJT01 DT_LEFT = #0000, DT_TOP = #0000, DT_RIGHT = #0002, DT_BOTTOM = #0008, DT_WORDBREAK = #0010, DT_EXPANDTABS = #0040, DT_TABSTOP = #0080, DT_NOCLIP = #0100, DT_EXTERNALLEADING = #0200, DT_CALCRECT = #0400, DT_NOPREFIX = #0800, DT_INTERNAL = #1000, DT_EDITCONTROL = #2000, DT_PATH_ELLIPSIS = #4000, DT_END_ELLIPSIS = #8000, DT_MODIFYSTRING = #10000, DT_RTLREADING = #20000, DT_WORD_ELLIPSIS = #40000, DT_NOFULLWIDTHCHARBREAK = #80000, DT_HIDEPREFIX = #100000, DT_PREFIXONLY = #200000, -- End CJT01 And finally the function at the END of Win32Lib: -- Beg CJT01 global function DrawText(atom id, sequence text, atom text_len, sequence format_dim, atom format_opt, object addl_format) sequence rtn_val atom hDc, text_buf, dtparms, ok, rect integer text_height -- Get the DC of the passed id hDc = getDC(id) putFontIntoHDC(hDc, id) -- Allocate the string text_buf = acquire_mem(0, text) -- Allocate a buffer to hold the rectangle information rect = acquire_mem( 0, SIZEOF_RECT ) store( rect, rectLeft, format_dim[1] ) store( rect, rectTop, format_dim[2] ) store( rect, rectRight, format_dim[3] ) store( rect, rectBottom, format_dim[4] ) dtparms = 0 text_height = w32Func(xDrawTextEx, {hDc, text_buf, text_len, rect, format_opt, dtparms}) -- Now get the values that can be modified by DrawTextEx rtn_val = {} rtn_val = append(rtn_val, text_height) rtn_val = append(rtn_val, peek_string(text_buf)) rtn_val = append(rtn_val, fetch(rect, rectLeft)) rtn_val = append(rtn_val, fetch(rect, rectTop)) rtn_val = append(rtn_val, fetch(rect, rectRight)) rtn_val = append(rtn_val, fetch(rect, rectBottom)) -- Release memory allocated release_mem(text_buf) release_mem(rect) if sequence(addl_format) then release_mem(dtparms) end if -- replace the font with the default again replaceObject( id, hDc, DefaultFontID, ForProgram ) -- Release the device context releaseDC( id ) return rtn_val end function -- End CJT01 Now you can try this out with this code. Of course you'll need to put my mods into Win32Lib for this to work. -- Beginning of code with trace include win32lib.ew constant JOB_HDG = "Job: ", DATE_HDG = "Date: ", TIME_HDG = "Time: ", PAGE_HDG = "Page: ", COMP_NAME = "Company Name", COMP_ADDR1 = "Address Line 1", COMP_ADDR2 = "City/State/Zip Code", ITEM_HDG = "Item", DESC_HDG = "Description", QTY_HDG = "Quantity", PRICE_HDG = "Price", EXT_HDG = "Extended", STATUS_HDG = "Status", QUOTE_TEXT = "This is a quote. It is only a quote. Had it been" & " an actual order you would have been notified where" & " and when to proceed. All prices quoted here are" & " completely arbitrary and subject to interpretation" & " of the quoter. You are not allowed to modify the" & " quote or question the content and/or validity of" & " the information contained in the quote. If you" & " have any questions do not call us as we are far too" & " busy to answer any of your pointless questions." global atom main, test main = create( Window, "Test Print", 0, 0, 0, 300, 150, 0) test = create( PushButton, "Test", main, 105, 45, 90, 30, 0) procedure print_job(atom jobheader_number) sequence print_parms, prt_dim, wrk_seq, extent, format_dim, result, rtn_text atom v_space, h_space, page, cur_x, cur_y, text_height, text_len prt_dim = {} wrk_seq = {} format_dim = {} page = 0 print_parms = getPrinter() if length(print_parms) then setFont(Printer, "Arial", 10, 0) setPenWidth(Printer, 6) prt_dim = getRect(Printer) h_space = floor(prt_dim[3] * .006) v_space = floor(prt_dim[4] * .003) if not startDoc("Test Print") then return end if if not startPage() then return end if page += 1 -- Draw the form drawRectangle(Printer, False, prt_dim[1], prt_dim[2], prt_dim[3], prt_dim[4]) cur_x = (prt_dim[1] + h_space) cur_y = (prt_dim[2] + v_space) setPenPos(Printer, cur_x, cur_y) wrk_seq = sprintf("%d", jobheader_number) wPuts(Printer, JOB_HDG & wrk_seq) cur_x = (floor(prt_dim[3] * .5)) setPenPos(Printer, cur_x , cur_y) wPuts(Printer, DATE_HDG) cur_x = (floor(prt_dim[3] * .7)) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, TIME_HDG) cur_x = (floor(prt_dim[3] * .9)) setPenPos(Printer, cur_x, cur_y) wrk_seq = sprintf("%d", page) wPuts(Printer, PAGE_HDG & wrk_seq) -- Line after job/date/time/page cur_x = prt_dim[1] cur_y = (floor(prt_dim[4] * .03)) drawLine(Printer, cur_x, cur_y, prt_dim[3], floor(prt_dim[4] * 03)) -- Print company name cur_x = (cur_x + h_space) cur_y = (cur_y + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, COMP_NAME) extent = getTextExtent(Printer, COMP_NAME) cur_y = (cur_y + extent[2] + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, COMP_ADDR1) extent = getTextExtent(Printer, COMP_ADDR1) cur_y = (cur_y + extent[2] + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, COMP_ADDR2) -- Line seperating company/customer cur_x = floor(prt_dim[3] * .5) cur_y = floor(prt_dim[4] * .03) drawLine(Printer, cur_x, cur_y, floor(prt_dim[3] * .5), floor(prt_dim[4] * .125)) -- Line after company/customer cur_x = prt_dim[1] cur_y = floor(prt_dim[4] * .125) drawLine(Printer, cur_x, cur_y, prt_dim[3], floor(prt_dim[4] * .125)) -- Set format dimensions for draw text trace(1) format_dim = { (cur_x + h_space) , (cur_y + v_space) , (prt_dim[3] - h_space) , (cur_y + v_space) } text_len = length(QUOTE_TEXT) result = DrawText(Printer, QUOTE_TEXT, text_len, format_dim, or_all({DT_CALCRECT,DT_WORDBREAK}), 0) text_height = result[1] rtn_text = result[2] format_dim[1] = result[3] format_dim[2] = result[4] format_dim[3] = result[5] format_dim[4] = result[6] text_len = length(rtn_text) result = DrawText(Printer, rtn_text, text_len, format_dim, or_all({DT_WORDBREAK, DT_LEFT}), 0) -- Line after terms/conditions cur_y = floor(prt_dim[4] * .25) drawLine(Printer, cur_x, cur_y, prt_dim[3], floor(prt_dim[4] * .25)) -- Print item number heading cur_x = (cur_x + h_space) cur_y = (cur_y + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, ITEM_HDG) -- Line after item number column cur_x = floor(prt_dim[3] * .10) cur_y = floor(prt_dim[4] * .25) drawLine(Printer, cur_x, cur_y, floor(prt_dim[3] * .10), prt_dim[4]) -- Print item description heading cur_x = (cur_x + h_space) cur_y = (cur_y + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, DESC_HDG) -- Line after item desc column cur_x = floor(prt_dim[3] * .60) cur_y = floor(prt_dim[4] * .25) drawLine(Printer, cur_x, cur_y, floor(prt_dim[3] * .60), prt_dim[4]) -- Print item quantity heading cur_x = (cur_x + h_space) cur_y = (cur_y + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, QTY_HDG) -- Line after quantity column cur_x = floor(prt_dim[3] * .70) cur_y = floor(prt_dim[4] * .25) drawLine(Printer, cur_x, cur_y, floor(prt_dim[3] * .70), prt_dim[4]) -- Print item price heading cur_x = (cur_x + h_space) cur_y = (cur_y + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, PRICE_HDG) -- Line after price column cur_x = floor(prt_dim[3] * .80) cur_y = floor(prt_dim[4] * .25) drawLine(Printer, cur_x, cur_y, floor(prt_dim[3] * .80), prt_dim[4]) -- Print extended heading cur_x = (cur_x + h_space) cur_y = (cur_y + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, EXT_HDG) -- Line after extended amount column cur_x = floor(prt_dim[3] * .90) cur_y = floor(prt_dim[4] * .25) drawLine(Printer, cur_x, cur_y, floor(prt_dim[3] * .90), prt_dim[4]) -- Print extended heading cur_x = (cur_x + h_space) cur_y = (cur_y + v_space) setPenPos(Printer, cur_x, cur_y) wPuts(Printer, STATUS_HDG) -- Line after column headings cur_x = prt_dim[1] cur_y = floor(prt_dim[4] * .28) drawLine(Printer, cur_x, cur_y, prt_dim[3], floor(prt_dim[4] * .28)) if not endPage() then return end if if not endDoc() then return end if releasePrinter() end if end procedure procedure test_onClick() print_job(1) end procedure onClick[ test ] = routine_id("test_onClick") WinMain( main, Normal ) -- End of code Sorry about the length of this post but this routine really made my life easier. Feel free to test/comment/take apart/etc. Jonas