-- Copyright 2015 Paul Kulchenko, ZeroBrane LLC --------------------------------------------------------- local frame = ide:GetMainFrame() local margin = {top = 10, left = 10, bottom = 10, right = 10} local function printScaling(dc, printOut) local pageSizeMM_x, pageSizeMM_y = printOut:GetPageSizeMM() local ppiScr_x, ppiScr_y = printOut:GetPPIScreen() local ppiPrn_x, ppiPrn_y = printOut:GetPPIPrinter() local ppi_scale_x = ppiPrn_x/ppiScr_x local ppi_scale_y = ppiPrn_y/ppiScr_y -- get the size of DC in pixels and the number of pixels in the page local dcSize_x, dcSize_y = dc:GetSize() local pagePixSize_x, pagePixSize_y = printOut:GetPageSizePixels() local dc_pagepix_scale_x = dcSize_x/pagePixSize_x local dc_pagepix_scale_y = dcSize_y/pagePixSize_y local dc_scale_x = ppi_scale_x * dc_pagepix_scale_x local dc_scale_y = ppi_scale_y * dc_pagepix_scale_y -- calculate the pixels / mm (25.4 mm = 1 inch) local ppmm_x = ppiScr_x / 25.4 local ppmm_y = ppiScr_y / 25.4 -- adjust the page size for the pixels / mm scaling factor local page_x = math.floor(pageSizeMM_x * ppmm_x) local page_y = math.floor(pageSizeMM_y * ppmm_y) local pageRect = wx.wxRect(0, 0, page_x, page_y) -- get margins informations and convert to printer pixels local top = math.floor(margin.top * ppmm_y) local bottom = math.floor(margin.bottom * ppmm_y) local left = math.floor(margin.left * ppmm_x) local right = math.floor(margin.right * ppmm_x) dc:SetUserScale(dc_scale_x, dc_scale_y) local printRect = wx.wxRect(left, top, page_x-(left+right), page_y-(top+bottom)) return printRect, pageRect end local function connectPrintEvents(printer, printOut) local editor = ide:GetEditorWithFocus() local cfg = ide.config.print local pages function printOut:OnPrintPage(pageNum) local dc = self:GetDC() local printRect, pageRect = printScaling(dc, printOut) -- print to an area smaller by the height of the header/footer dc:SetFont(editor:GetFont()) local _, headerHeight = dc:GetTextExtent("qH") local textRect = wx.wxRect(printRect) if cfg.header then textRect:SetY(textRect:GetY() + headerHeight*1.5) textRect:SetHeight(textRect:GetHeight() - headerHeight*1.5) end if cfg.footer then textRect:SetHeight(textRect:GetHeight() - headerHeight*1.5) end local selection = printer:GetPrintDialogData():GetSelection() local spos = selection and editor:GetSelectionStart() or 1 local epos = selection and editor:GetSelectionEnd() or editor:GetLength() if pageNum == nil then pages = {} ide:PushStatus("") printOut.startTime = wx.wxNow() local pos = spos while pos < epos do table.insert(pages, pos) pos = editor:FormatRange(false, pos, epos, dc, dc, textRect, pageRect) ide:PopStatus() ide:PushStatus(TR("%s%% formatted..."):format(math.floor((pos-spos)*100.0/(epos-spos)))) end if #pages == 0 then pages = {0} end ide:PopStatus() else ide:SetStatusFor(TR("Formatting page %d..."):format(pageNum)) editor:FormatRange(true, pages[pageNum], epos, dc, dc, textRect, pageRect) local c = wx.wxColour(127, 127, 127) dc:SetPen(wx.wxPen(c, 1, wx.wxSOLID)) dc:SetTextForeground(c) local doc = ide:GetDocument(editor) local format = "([^\t]*)\t?([^\t]*)\t?([^\t]*)" local placeholders = { D = printOut.startTime, p = pageNum, P = #pages, S = doc and doc:GetFileName() or "", } dc:SetFont(editor:GetFont()) if cfg.header then local left, center, right = ExpandPlaceholders(cfg.header, placeholders):match(format) dc:DrawText(left, printRect.X, printRect.Y) dc:DrawText(center, printRect.Left + (printRect.Left + printRect.Width - dc:GetTextExtentSize(center).Width)/2, printRect.Y) dc:DrawText(right, printRect.Left + printRect.Width - dc:GetTextExtentSize(right).Width, printRect.Y) dc:DrawLine(printRect.X, printRect.Y + headerHeight, printRect.Left + printRect.Width, printRect.Y + headerHeight) end if cfg.footer then local footerY = printRect.Y + printRect.Height - headerHeight local left, center, right = ExpandPlaceholders(cfg.footer, placeholders):match(format) dc:DrawText(left, printRect.X, footerY) dc:DrawText(center, printRect.Left + (printRect.Left + printRect.Width - dc:GetTextExtentSize(center).Width)/2, footerY) dc:DrawText(right, printRect.Left + printRect.Width - dc:GetTextExtentSize(right).Width, footerY) dc:DrawLine(printRect.X, footerY, printRect.Left + printRect.Width, footerY) end end return true end function printOut:HasPage(pageNum) return pages[pageNum] ~= nil end function printOut:GetPageInfo() -- on Linux `GetPageInfo` is called before the canvas is initialized, which prevents -- proper calculation of the number of pages (wx2.9.5). -- Return defaults here as it's going to be called once more in the right place. if ide.osname == "Unix" and not pages then return 1, 9999, 1, 9999 end local printDD = printer:GetPrintDialogData() -- due to wxwidgets bug (http://trac.wxwidgets.org/ticket/17200), if `to` page is not set explicitly, -- only one page is being printed when `selection` option is selected in the print dialog. if printDD:GetSelection() then printDD:SetToPage(#pages) end -- set the page as a workaround local tofrom = not printDD:GetSelection() and not printDD:GetAllPages() return 1, #pages, tofrom and printDD:GetFromPage() or 1, tofrom and printDD:GetToPage() or #pages end function printOut:OnPreparePrinting() self:OnPrintPage() end end frame:Connect(ID_PAGESETUP, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) local pageSetupDD = wx.wxPageSetupDialogData() pageSetupDD.MarginTopLeft = wx.wxPoint(margin.left, margin.top) pageSetupDD.MarginBottomRight = wx.wxPoint(margin.right, margin.bottom) pageSetupDD:EnableOrientation(false) pageSetupDD:EnablePaper(false) local pageSetupDialog = wx.wxPageSetupDialog(frame, pageSetupDD) pageSetupDialog:ShowModal() pageSetupDD = pageSetupDialog:GetPageSetupDialogData() margin.top, margin.left = pageSetupDD.MarginTopLeft.y, pageSetupDD.MarginTopLeft.x margin.bottom, margin.right = pageSetupDD.MarginBottomRight.y, pageSetupDD.MarginBottomRight.x end) frame:Connect(ID_PRINT, wx.wxEVT_COMMAND_MENU_SELECTED, function (event) local cfg = ide.config.print local editor = ide:GetEditorWithFocus() editor:SetPrintMagnification(cfg.magnification) editor:SetPrintColourMode(cfg.colourmode) editor:SetPrintWrapMode(cfg.wrapmode) -- only enable selection if there is something selected in the editor (ignore multiple selections) local printDD = wx.wxPrintDialogData() printDD:EnableSelection(editor:GetSelectionStart() ~= editor:GetSelectionEnd()) local printer = wx.wxPrinter(printDD) local luaPrintout = wx.wxLuaPrintout() connectPrintEvents(printer, luaPrintout) -- save and hide indicators local indics = {} for _, num in pairs(ide:GetIndicators()) do indics[num] = editor:IndicatorGetStyle(num) editor:IndicatorSetStyle(num, wxstc.wxSTC_INDIC_HIDDEN) end -- bold keywords local keywords = {} for _, num in ipairs(ide:IsValidProperty(editor, 'spec') and editor.spec.lexerstyleconvert and editor.spec.lexerstyleconvert.keywords0 or {}) do keywords[num] = editor:StyleGetBold(num) editor:StyleSetBold(num, true) end local ok = printer:Print(frame, luaPrintout, true) -- restore indicators for n, style in pairs(indics) do editor:IndicatorSetStyle(n, style) end for n, style in pairs(keywords) do editor:StyleSetBold(n, style) end if not ok and printer:GetLastError() == wx.wxPRINTER_ERROR then ReportError("There was a problem while printing.\nCheck if your current printer is set correctly.") end end) frame:Connect(ID_PRINT, wx.wxEVT_UPDATE_UI, function(event) event:Enable(ide:GetEditorWithFocus() ~= nil) end) local _, menu, epos = ide:FindMenuItem(ID.EXIT) -- disable printing on Unix/Linux as it generates incorrect layout (wx2.9.5, wx3.1) if ide.osname ~= "Unix" and menu and epos then -- insert Print-repated menu items (going in the opposite order) menu:Insert(epos-1, ID_PAGESETUP, TR("Page Setup..."), "") menu:Insert(epos-1, ID_PRINT, TR("&Print..."), TR("Print the current document")) menu:InsertSeparator(epos-1) end