/* * Wordpad implementation - Printing and print preview functions * * Copyright 2007 by Alexander N. Sørnes * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include "wordpad.h" typedef struct _previewinfo { int page; int pages; HDC hdc; HDC hdcSized; RECT window; LPWSTR wszFileName; } previewinfo, *ppreviewinfo; static HGLOBAL devMode; static HGLOBAL devNames; static RECT margins; static previewinfo preview; static const WCHAR var_pagemargin[] = {'P','a','g','e','M','a','r','g','i','n',0}; static LPWSTR get_print_file_filter(HWND hMainWnd) { static WCHAR wszPrintFilter[MAX_STRING_LEN*2+6+4+1]; const WCHAR files_prn[] = {'*','.','P','R','N',0}; const WCHAR files_all[] = {'*','.','*','\0'}; LPWSTR p; HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hMainWnd, GWLP_HINSTANCE); p = wszPrintFilter; LoadStringW(hInstance, STRING_PRINTER_FILES_PRN, p, MAX_STRING_LEN); p += lstrlenW(p) + 1; lstrcpyW(p, files_prn); p += lstrlenW(p) + 1; LoadStringW(hInstance, STRING_ALL_FILES, p, MAX_STRING_LEN); p += lstrlenW(p) + 1; lstrcpyW(p, files_all); p += lstrlenW(p) + 1; *p = 0; return wszPrintFilter; } void registry_set_pagemargins(HKEY hKey) { RegSetValueExW(hKey, var_pagemargin, 0, REG_BINARY, (LPBYTE)&margins, sizeof(RECT)); } void registry_read_pagemargins(HKEY hKey) { DWORD size = sizeof(RECT); if(!hKey || RegQueryValueExW(hKey, var_pagemargin, 0, NULL, (LPBYTE)&margins, &size) != ERROR_SUCCESS || size != sizeof(RECT)) { margins.top = 1417; margins.bottom = 1417; margins.left = 1757; margins.right = 1757; } } static void AddTextButton(HWND hRebarWnd, int string, int command, int id) { REBARBANDINFOW rb; HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hRebarWnd, GWLP_HINSTANCE); WCHAR text[MAX_STRING_LEN]; HWND hButton; LoadStringW(hInstance, string, text, MAX_STRING_LEN); hButton = CreateWindowW(WC_BUTTONW, text, WS_VISIBLE | WS_CHILD, 5, 5, 100, 15, hRebarWnd, (HMENU)command, hInstance, NULL); rb.cbSize = sizeof(rb); rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID; rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT; rb.hwndChild = hButton; rb.cyChild = rb.cyMinChild = 22; rb.cx = rb.cxMinChild = 90; rb.cxIdeal = 100; rb.wID = id; SendMessageW(hRebarWnd, RB_INSERTBAND, -1, (LPARAM)&rb); } static HDC make_dc(void) { if(devNames && devMode) { LPDEVNAMES dn = GlobalLock(devNames); LPDEVMODEW dm = GlobalLock(devMode); HDC ret; ret = CreateDCW((LPWSTR)dn + dn->wDriverOffset, (LPWSTR)dn + dn->wDeviceOffset, 0, dm); GlobalUnlock(dn); GlobalUnlock(dm); return ret; } else { return 0; } } static LONG twips_to_centmm(int twips) { return MulDiv(twips, 1000, 567); } LONG centmm_to_twips(int mm) { return MulDiv(mm, 567, 1000); } static LONG twips_to_pixels(int twips, int dpi) { float ret = ((float)twips / ((float)567 * 2.54)) * (float)dpi; return (LONG)ret; } static LONG devunits_to_twips(int units, int dpi) { float ret = ((float)units / (float)dpi) * (float)567 * 2.54; return (LONG)ret; } static RECT get_print_rect(HDC hdc) { RECT rc; int width, height; if(hdc) { int dpiY = GetDeviceCaps(hdc, LOGPIXELSY); int dpiX = GetDeviceCaps(hdc, LOGPIXELSX); width = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALWIDTH), dpiX); height = devunits_to_twips(GetDeviceCaps(hdc, PHYSICALHEIGHT), dpiY); } else { width = centmm_to_twips(18500); height = centmm_to_twips(27000); } rc.left = margins.left; rc.right = width - margins.right; rc.top = margins.top; rc.bottom = height - margins.bottom; return rc; } void target_device(HWND hMainWnd, DWORD wordWrap) { HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR); HDC hdc = make_dc(); int width = 0; if(wordWrap == ID_WORDWRAP_MARGIN) { RECT rc = get_print_rect(hdc); width = rc.right - rc.left; } if(!hdc) { HDC hMaindc = GetDC(hMainWnd); hdc = CreateCompatibleDC(hMaindc); ReleaseDC(hMainWnd, hMaindc); } SendMessageW(hEditorWnd, EM_SETTARGETDEVICE, (WPARAM)hdc, width); DeleteDC(hdc); } static LPWSTR dialog_print_to_file(HWND hMainWnd) { OPENFILENAMEW ofn; static WCHAR file[MAX_PATH] = {'O','U','T','P','U','T','.','P','R','N',0}; static const WCHAR defExt[] = {'P','R','N',0}; static LPWSTR file_filter; if(!file_filter) file_filter = get_print_file_filter(hMainWnd); ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT; ofn.hwndOwner = hMainWnd; ofn.lpstrFilter = file_filter; ofn.lpstrFile = (LPWSTR)file; ofn.nMaxFile = MAX_PATH; ofn.lpstrDefExt = (LPWSTR)defExt; if(GetSaveFileNameW(&ofn)) return (LPWSTR)file; else return FALSE; } static int get_num_pages(HWND hEditorWnd, FORMATRANGE fr) { int page = 0; do { page++; fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr); } while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax); return page; } static void char_from_pagenum(HWND hEditorWnd, FORMATRANGE *fr, int page) { int i; for(i = 1; i <= page; i++) { if(i == page) break; fr->chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)fr); } } static void print(LPPRINTDLGW pd, LPWSTR wszFileName) { FORMATRANGE fr; DOCINFOW di; HWND hEditorWnd = GetDlgItem(pd->hwndOwner, IDC_EDITOR); int printedPages = 0; fr.hdc = pd->hDC; fr.hdcTarget = pd->hDC; fr.rc = get_print_rect(fr.hdc); fr.rcPage.left = 0; fr.rcPage.right = fr.rc.right + margins.right; fr.rcPage.top = 0; fr.rcPage.bottom = fr.rc.bottom + margins.bottom; ZeroMemory(&di, sizeof(di)); di.cbSize = sizeof(di); di.lpszDocName = wszFileName; if(pd->Flags & PD_PRINTTOFILE) { di.lpszOutput = dialog_print_to_file(pd->hwndOwner); if(!di.lpszOutput) return; } if(pd->Flags & PD_SELECTION) { SendMessageW(hEditorWnd, EM_EXGETSEL, 0, (LPARAM)&fr.chrg); } else { GETTEXTLENGTHEX gt; gt.flags = GTL_DEFAULT; gt.codepage = 1200; fr.chrg.cpMin = 0; fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0); if(pd->Flags & PD_PAGENUMS) char_from_pagenum(hEditorWnd, &fr, pd->nToPage); } StartDocW(fr.hdc, &di); do { if(StartPage(fr.hdc) <= 0) break; fr.chrg.cpMin = SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr); if(EndPage(fr.hdc) <= 0) break; printedPages++; if((pd->Flags & PD_PAGENUMS) && (printedPages > (pd->nToPage - pd->nFromPage))) break; } while(fr.chrg.cpMin && fr.chrg.cpMin < fr.chrg.cpMax); EndDoc(fr.hdc); SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0); } void dialog_printsetup(HWND hMainWnd) { PAGESETUPDLGW ps; ZeroMemory(&ps, sizeof(ps)); ps.lStructSize = sizeof(ps); ps.hwndOwner = hMainWnd; ps.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS; ps.rtMargin.left = twips_to_centmm(margins.left); ps.rtMargin.right = twips_to_centmm(margins.right); ps.rtMargin.top = twips_to_centmm(margins.top); ps.rtMargin.bottom = twips_to_centmm(margins.bottom); ps.hDevMode = devMode; ps.hDevNames = devNames; if(PageSetupDlgW(&ps)) { margins.left = centmm_to_twips(ps.rtMargin.left); margins.right = centmm_to_twips(ps.rtMargin.right); margins.top = centmm_to_twips(ps.rtMargin.top); margins.bottom = centmm_to_twips(ps.rtMargin.bottom); devMode = ps.hDevMode; devNames = ps.hDevNames; } } void get_default_printer_opts(void) { PRINTDLGW pd; ZeroMemory(&pd, sizeof(pd)); ZeroMemory(&pd, sizeof(pd)); pd.lStructSize = sizeof(pd); pd.Flags = PD_RETURNDC | PD_RETURNDEFAULT; pd.hDevMode = devMode; PrintDlgW(&pd); devMode = pd.hDevMode; devNames = pd.hDevNames; } void print_quick(LPWSTR wszFileName) { PRINTDLGW pd; ZeroMemory(&pd, sizeof(pd)); pd.hDC = make_dc(); print(&pd, wszFileName); } void dialog_print(HWND hMainWnd, LPWSTR wszFileName) { PRINTDLGW pd; HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR); int from = 0; int to = 0; ZeroMemory(&pd, sizeof(pd)); pd.lStructSize = sizeof(pd); pd.hwndOwner = hMainWnd; pd.Flags = PD_RETURNDC | PD_USEDEVMODECOPIESANDCOLLATE; pd.nMinPage = 1; pd.nMaxPage = -1; pd.hDevMode = devMode; pd.hDevNames = devNames; SendMessageW(hEditorWnd, EM_GETSEL, (WPARAM)&from, (LPARAM)&to); if(from == to) pd.Flags |= PD_NOSELECTION; if(PrintDlgW(&pd)) { devMode = pd.hDevMode; devNames = pd.hDevNames; print(&pd, wszFileName); } } static void preview_bar_show(HWND hMainWnd, BOOL show) { HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR); int i; if(show) { REBARBANDINFOW rb; HWND hStatic; AddTextButton(hReBar, STRING_PREVIEW_PRINT, ID_PRINT, BANDID_PREVIEW_BTN1); AddTextButton(hReBar, STRING_PREVIEW_NEXTPAGE, ID_PREVIEW_NEXTPAGE, BANDID_PREVIEW_BTN2); AddTextButton(hReBar, STRING_PREVIEW_PREVPAGE, ID_PREVIEW_PREVPAGE, BANDID_PREVIEW_BTN3); AddTextButton(hReBar, STRING_PREVIEW_CLOSE, ID_FILE_EXIT, BANDID_PREVIEW_BTN4); hStatic = CreateWindowW(WC_STATICW, NULL, WS_VISIBLE | WS_CHILD, 0, 0, 0, 0, hReBar, NULL, NULL, NULL); rb.cbSize = sizeof(rb); rb.fMask = RBBIM_SIZE | RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_IDEALSIZE | RBBIM_ID; rb.fStyle = RBBS_NOGRIPPER | RBBS_VARIABLEHEIGHT; rb.hwndChild = hStatic; rb.cyChild = rb.cyMinChild = 22; rb.cx = rb.cxMinChild = 90; rb.cxIdeal = 100; rb.wID = BANDID_PREVIEW_BUFFER; SendMessageW(hReBar, RB_INSERTBAND, -1, (LPARAM)&rb); } else { for(i = 0; i <= PREVIEW_BUTTONS; i++) SendMessageW(hReBar, RB_DELETEBAND, SendMessageW(hReBar, RB_IDTOINDEX, BANDID_PREVIEW_BTN1+i, 0), 0); } } void init_preview(HWND hMainWnd, LPWSTR wszFileName) { preview.page = 1; preview.hdc = 0; preview.wszFileName = wszFileName; preview_bar_show(hMainWnd, TRUE); } void close_preview(HWND hMainWnd) { preview.window.right = 0; preview.window.bottom = 0; preview.page = 0; preview.pages = 0; preview_bar_show(hMainWnd, FALSE); } BOOL preview_isactive(void) { return preview.page != 0; } LRESULT print_preview(HWND hMainWnd) { FORMATRANGE fr; GETTEXTLENGTHEX gt; HDC hdc; RECT window, background; HBITMAP hBitmapCapture, hBitmapScaled; int bmWidth, bmHeight, bmNewWidth, bmNewHeight; float ratioWidth, ratioHeight, ratio; int xOffset, yOffset; int barheight; HWND hReBar = GetDlgItem(hMainWnd, IDC_REBAR); PAINTSTRUCT ps; hdc = BeginPaint(hMainWnd, &ps); GetClientRect(hMainWnd, &window); fr.hdcTarget = make_dc(); fr.rc = get_print_rect(fr.hdcTarget); fr.rcPage.left = 0; fr.rcPage.top = 0; fr.rcPage.bottom = fr.rc.bottom + margins.bottom; fr.rcPage.right = fr.rc.right + margins.right; bmWidth = twips_to_pixels(fr.rcPage.right, GetDeviceCaps(hdc, LOGPIXELSX)); bmHeight = twips_to_pixels(fr.rcPage.bottom, GetDeviceCaps(hdc, LOGPIXELSY)); hBitmapCapture = CreateCompatibleBitmap(hdc, bmWidth, bmHeight); if(!preview.hdc) { RECT paper; HWND hEditorWnd = GetDlgItem(hMainWnd, IDC_EDITOR); preview.hdc = CreateCompatibleDC(hdc); fr.hdc = preview.hdc; gt.flags = GTL_DEFAULT; gt.codepage = 1200; fr.chrg.cpMin = 0; fr.chrg.cpMax = SendMessageW(hEditorWnd, EM_GETTEXTLENGTHEX, (WPARAM)>, 0); paper.left = 0; paper.right = bmWidth; paper.top = 0; paper.bottom = bmHeight; if(!preview.pages) preview.pages = get_num_pages(hEditorWnd, fr); SelectObject(preview.hdc, hBitmapCapture); char_from_pagenum(hEditorWnd, &fr, preview.page); FillRect(preview.hdc, &paper, GetStockObject(WHITE_BRUSH)); SendMessageW(hEditorWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr); SendMessageW(hEditorWnd, EM_FORMATRANGE, FALSE, 0); EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_PREVPAGE), preview.page > 1); EnableWindow(GetDlgItem(hReBar, ID_PREVIEW_NEXTPAGE), preview.page < preview.pages); } barheight = SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0); ratioWidth = ((float)window.right - 20.0) / (float)bmHeight; ratioHeight = ((float)window.bottom - 20.0 - (float)barheight) / (float)bmHeight; if(ratioWidth > ratioHeight) ratio = ratioHeight; else ratio = ratioWidth; bmNewWidth = (int)((float)bmWidth * ratio); bmNewHeight = (int)((float)bmHeight * ratio); hBitmapScaled = CreateCompatibleBitmap(hdc, bmNewWidth, bmNewHeight); xOffset = ((window.right - bmNewWidth) / 2); yOffset = ((window.bottom - bmNewHeight + barheight) / 2); if(window.right != preview.window.right || window.bottom != preview.window.bottom) { HPEN hPen; int TopMargin = (int)((float)twips_to_pixels(fr.rc.top, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio); int BottomMargin = (int)((float)twips_to_pixels(fr.rc.bottom, GetDeviceCaps(hdc, LOGPIXELSX)) * ratio); int LeftMargin = (int)((float)twips_to_pixels(fr.rc.left, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio); int RightMargin = (int)((float)twips_to_pixels(fr.rc.right, GetDeviceCaps(hdc, LOGPIXELSY)) * ratio); DeleteDC(preview.hdcSized); preview.hdcSized = CreateCompatibleDC(hdc); SelectObject(preview.hdcSized, hBitmapScaled); StretchBlt(preview.hdcSized, 0, 0, bmNewWidth, bmNewHeight, preview.hdc, 0, 0, bmWidth, bmHeight, SRCCOPY); /* Draw margin lines */ hPen = CreatePen(PS_DOT, 1, RGB(0,0,0)); SelectObject(preview.hdcSized, hPen); MoveToEx(preview.hdcSized, 0, TopMargin, NULL); LineTo(preview.hdcSized, bmNewWidth, TopMargin); MoveToEx(preview.hdcSized, 0, BottomMargin, NULL); LineTo(preview.hdcSized, bmNewWidth, BottomMargin); MoveToEx(preview.hdcSized, LeftMargin, 0, NULL); LineTo(preview.hdcSized, LeftMargin, bmNewHeight); MoveToEx(preview.hdcSized, RightMargin, 0, NULL); LineTo(preview.hdcSized, RightMargin, bmNewHeight); } window.top = barheight; FillRect(hdc, &window, GetStockObject(GRAY_BRUSH)); SelectObject(hdc, hBitmapScaled); background.left = xOffset - 2; background.right = xOffset + bmNewWidth + 2; background.top = yOffset - 2; background.bottom = yOffset + bmNewHeight + 2; FillRect(hdc, &background, GetStockObject(BLACK_BRUSH)); BitBlt(hdc, xOffset, yOffset, bmNewWidth, bmNewHeight, preview.hdcSized, 0, 0, SRCCOPY); DeleteDC(fr.hdcTarget); preview.window = window; EndPaint(hMainWnd, &ps); return 0; } LRESULT preview_command(HWND hWnd, WPARAM wParam, LPARAM lParam) { switch(LOWORD(wParam)) { case ID_FILE_EXIT: PostMessageW(hWnd, WM_CLOSE, 0, 0); break; case ID_PREVIEW_NEXTPAGE: case ID_PREVIEW_PREVPAGE: { HWND hReBar = GetDlgItem(hWnd, IDC_REBAR); RECT rc; if(LOWORD(wParam) == ID_PREVIEW_NEXTPAGE) preview.page++; else preview.page--; preview.hdc = 0; preview.window.right = 0; GetClientRect(hWnd, &rc); rc.top += SendMessageW(hReBar, RB_GETBARHEIGHT, 0, 0); InvalidateRect(hWnd, &rc, TRUE); } break; case ID_PRINT: dialog_print(hWnd, preview.wszFileName); SendMessageW(hWnd, WM_CLOSE, 0, 0); break; } return 0; }