void CopySelectionToClipboard(WindowInfo *win) { if (!win->selectionOnPage) return; CrashIf(win->selectionOnPage->Count() == 0); if (win->selectionOnPage->Count() == 0) return; CrashIf(!win->dm || !win->dm->engine); if (!win->dm || !win->dm->engine) return; if (!OpenClipboard(NULL)) return; EmptyClipboard(); #ifndef DISABLE_DOCUMENT_RESTRICTIONS if (!win->dm->engine->AllowsCopyingText()) ShowNotification(win, _TR("Copying text was denied (copying as image only)")); else #endif if (!win->dm->engine->IsImageCollection()) { ScopedMem<WCHAR> selText; bool isTextSelection = win->dm->textSelection->result.len > 0; if (isTextSelection) { selText.Set(win->dm->textSelection->ExtractText(L"\r\n")); } else { WStrVec selections; for (size_t i = 0; i < win->selectionOnPage->Count(); i++) { SelectionOnPage *selOnPage = &win->selectionOnPage->At(i); WCHAR *text = win->dm->GetTextInRegion(selOnPage->pageNo, selOnPage->rect); if (text) selections.Push(text); } selText.Set(selections.Join()); } // don't copy empty text if (!str::IsEmpty(selText.Get())) CopyTextToClipboard(selText, true); if (isTextSelection) { // don't also copy the first line of a text selection as an image CloseClipboard(); return; } } /* also copy a screenshot of the current selection to the clipboard */ SelectionOnPage *selOnPage = &win->selectionOnPage->At(0); RenderedBitmap * bmp = win->dm->engine->RenderBitmap(selOnPage->pageNo, win->dm->ZoomReal(), win->dm->Rotation(), &selOnPage->rect, Target_Export); if (bmp) CopyImageToClipboard(bmp->GetBitmap(), true); delete bmp; CloseClipboard(); }
static BaseEngine *ps2pdf(const WCHAR *fileName) { // TODO: read from gswin32c's stdout instead of using a TEMP file ScopedMem<WCHAR> shortPath(path::ShortPath(fileName)); ScopedMem<WCHAR> tmpFile(path::GetTempPath(L"PsE")); ScopedFile tmpFileScope(tmpFile); ScopedMem<WCHAR> gswin32c(GetGhostscriptPath()); if (!shortPath || !tmpFile || !gswin32c) return nullptr; // try to help Ghostscript determine the intended page size ScopedMem<WCHAR> psSetup; RectI page = ExtractDSCPageSize(fileName); if (!page.IsEmpty()) psSetup.Set(str::Format(L" << /PageSize [%i %i] >> setpagedevice", page.dx, page.dy)); ScopedMem<WCHAR> cmdLine(str::Format( L"\"%s\" -q -dSAFER -dNOPAUSE -dBATCH -dEPSCrop -sOutputFile=\"%s\" -sDEVICE=pdfwrite -c \".setpdfwrite%s\" -f \"%s\"", gswin32c.Get(), tmpFile.Get(), psSetup ? psSetup.Get() : L"", shortPath.Get())); fprintf(stderr, "- %s:%d: using '%ls' for creating '%%TEMP%%\\%ls'\n", path::GetBaseName(__FILE__), __LINE__, gswin32c.Get(), path::GetBaseName(tmpFile)); // TODO: the PS-to-PDF conversion can hang the UI for several seconds HANDLE process = LaunchProcess(cmdLine, nullptr, CREATE_NO_WINDOW); if (!process) return nullptr; DWORD timeout = 10000; #ifdef DEBUG // allow to disable the timeout for debugging purposes if (GetEnvironmentVariable(L"SUMATRAPDF_NO_GHOSTSCRIPT_TIMEOUT", nullptr, 0)) timeout = INFINITE; #endif DWORD exitCode = EXIT_FAILURE; WaitForSingleObject(process, timeout); GetExitCodeProcess(process, &exitCode); TerminateProcess(process, 1); CloseHandle(process); if (exitCode != EXIT_SUCCESS) return nullptr; size_t len; ScopedMem<char> pdfData(file::ReadAll(tmpFile, &len)); if (!pdfData) return nullptr; ScopedComPtr<IStream> stream(CreateStreamFromData(pdfData, len)); if (!stream) return nullptr; return PdfEngine::CreateFromStream(stream); }
// Detect TeX editors installed on the system and construct the // corresponding inverse search commands. // // Parameters: // hwndCombo -- (optional) handle to a combo list that will be filled with the list of possible inverse search commands. // Returns: // the inverse search command of the first detected editor (the caller needs to free() the result). WCHAR *AutoDetectInverseSearchCommands(HWND hwndCombo) { WCHAR *firstEditor = nullptr; WStrList foundExes; for (int i = 0; i < dimof(editor_rules); i++) { ScopedMem<WCHAR> path(ReadRegStr(editor_rules[i].RegRoot, editor_rules[i].RegKey, editor_rules[i].RegValue)); if (!path) continue; ScopedMem<WCHAR> exePath; if (editor_rules[i].Type == SiblingPath) { // remove file part ScopedMem<WCHAR> dir(path::GetDir(path)); exePath.Set(path::Join(dir, editor_rules[i].BinaryFilename)); } else if (editor_rules[i].Type == BinaryDir) exePath.Set(path::Join(path, editor_rules[i].BinaryFilename)); else // if (editor_rules[i].Type == BinaryPath) exePath.Set(path.StealData()); // don't show duplicate entries if (foundExes.FindI(exePath) != -1) continue; // don't show inexistent paths (and don't try again for them) if (!file::Exists(exePath)) { foundExes.Append(exePath.StealData()); continue; } ScopedMem<WCHAR> editorCmd(str::Format(L"\"%s\" %s", exePath.Get(), editor_rules[i].InverseSearchArgs)); if (!hwndCombo) { // no need to fill a combo box: return immeditately after finding an editor. return editorCmd.StealData(); } ComboBox_AddString(hwndCombo, editorCmd); if (!firstEditor) firstEditor = editorCmd.StealData(); foundExes.Append(exePath.StealData()); } // Fall back to notepad as a default handler if (!firstEditor) { firstEditor = str::Dup(L"notepad %f"); if (hwndCombo) ComboBox_AddString(hwndCombo, firstEditor); } return firstEditor; }
HPROPSHEETPAGE CreatePrintAdvancedPropSheet(Print_Advanced_Data *data, ScopedMem<DLGTEMPLATE>& dlgTemplate) { PROPSHEETPAGE psp; ZeroMemory(&psp, sizeof(PROPSHEETPAGE)); psp.dwSize = sizeof(PROPSHEETPAGE); psp.dwFlags = PSP_USETITLE | PSP_PREMATURE; psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPSHEET_PRINT_ADVANCED); psp.pfnDlgProc = Sheet_Print_Advanced_Proc; psp.lParam = (LPARAM)data; psp.pszTitle = _TR("Advanced"); if (IsUIRightToLeft()) { dlgTemplate.Set(GetRtLDlgTemplate(IDD_PROPSHEET_PRINT_ADVANCED)); psp.pResource = dlgTemplate.Get(); psp.dwFlags |= PSP_DLGINDIRECT; } return CreatePropertySheetPage(&psp); }
void OnMenuPrint(WindowInfo *win, bool waitForCompletion) { // we remember some printer settings per process static ScopedMem<DEVMODE> defaultDevMode; static PrintScaleAdv defaultScaleAdv = PrintScaleShrink; static bool defaultAsImage = false; bool printSelection = false; Vec<PRINTPAGERANGE> ranges; PRINTER_INFO_2 printerInfo = { 0 }; if (!HasPermission(Perm_PrinterAccess)) return; DisplayModel *dm = win->dm; assert(dm); if (!dm) return; if (!dm->engine || !dm->engine->AllowsPrinting()) return; if (win->IsChm()) { win->dm->AsChmEngine()->PrintCurrentPage(); return; } if (win->printThread) { int res = MessageBox(win->hwndFrame, _TR("Printing is still in progress. Abort and start over?"), _TR("Printing in progress."), MB_ICONEXCLAMATION | MB_YESNO | (IsUIRightToLeft() ? MB_RTLREADING : 0)); if (res == IDNO) return; } AbortPrinting(win); PRINTDLGEX pd; ZeroMemory(&pd, sizeof(PRINTDLGEX)); pd.lStructSize = sizeof(PRINTDLGEX); pd.hwndOwner = win->hwndFrame; pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_COLLATE; if (!win->selectionOnPage) pd.Flags |= PD_NOSELECTION; pd.nCopies = 1; /* by default print all pages */ pd.nPageRanges = 1; pd.nMaxPageRanges = MAXPAGERANGES; PRINTPAGERANGE *ppr = AllocArray<PRINTPAGERANGE>(MAXPAGERANGES); pd.lpPageRanges = ppr; ppr->nFromPage = 1; ppr->nToPage = dm->PageCount(); pd.nMinPage = 1; pd.nMaxPage = dm->PageCount(); pd.nStartPage = START_PAGE_GENERAL; Print_Advanced_Data advanced(PrintRangeAll, defaultScaleAdv, defaultAsImage); ScopedMem<DLGTEMPLATE> dlgTemplate; // needed for RTL languages HPROPSHEETPAGE hPsp = CreatePrintAdvancedPropSheet(&advanced, dlgTemplate); pd.lphPropertyPages = &hPsp; pd.nPropertyPages = 1; // restore remembered settings if (defaultDevMode) pd.hDevMode = GlobalMemDup(defaultDevMode.Get(), defaultDevMode.Get()->dmSize + defaultDevMode.Get()->dmDriverExtra); if (PrintDlgEx(&pd) != S_OK) { if (CommDlgExtendedError() != 0) { /* if PrintDlg was cancelled then CommDlgExtendedError is zero, otherwise it returns the error code, which we could look at here if we wanted. for now just warn the user that printing has stopped becasue of an error */ MessageBox(win->hwndFrame, _TR("Couldn't initialize printer"), _TR("Printing problem."), MB_ICONEXCLAMATION | MB_OK | (IsUIRightToLeft() ? MB_RTLREADING : 0)); } goto Exit; } if (pd.dwResultAction == PD_RESULT_PRINT || pd.dwResultAction == PD_RESULT_APPLY) { // remember settings for this process LPDEVMODE devMode = (LPDEVMODE)GlobalLock(pd.hDevMode); if (devMode) { defaultDevMode.Set((LPDEVMODE)memdup(devMode, devMode->dmSize + devMode->dmDriverExtra)); GlobalUnlock(pd.hDevMode); } defaultScaleAdv = advanced.scale; defaultAsImage = advanced.asImage; } if (pd.dwResultAction != PD_RESULT_PRINT) goto Exit; if (pd.Flags & PD_CURRENTPAGE) { PRINTPAGERANGE pr = { dm->CurrentPageNo(), dm->CurrentPageNo() }; ranges.Append(pr); } else if (win->selectionOnPage && (pd.Flags & PD_SELECTION)) { printSelection = true; } else if (!(pd.Flags & PD_PAGENUMS)) { PRINTPAGERANGE pr = { 1, dm->PageCount() }; ranges.Append(pr); } else { assert(pd.nPageRanges > 0); for (DWORD i = 0; i < pd.nPageRanges; i++) ranges.Append(pd.lpPageRanges[i]); } LPDEVNAMES devNames = (LPDEVNAMES)GlobalLock(pd.hDevNames); LPDEVMODE devMode = (LPDEVMODE)GlobalLock(pd.hDevMode); if (devNames) { printerInfo.pDriverName = (LPWSTR)devNames + devNames->wDriverOffset; printerInfo.pPrinterName = (LPWSTR)devNames + devNames->wDeviceOffset; printerInfo.pPortName = (LPWSTR)devNames + devNames->wOutputOffset; } PrintData *data = new PrintData(dm->engine, &printerInfo, devMode, ranges, advanced, dm->Rotation(), printSelection ? win->selectionOnPage : NULL); if (devNames) GlobalUnlock(pd.hDevNames); if (devMode) GlobalUnlock(pd.hDevMode); if (!waitForCompletion) PrintToDeviceOnThread(win, data); else { PrintToDevice(*data); delete data; } Exit: free(ppr); GlobalFree(pd.hDevNames); GlobalFree(pd.hDevMode); }
// returns true if the double-click was handled and false if it wasn't bool OnInverseSearch(WindowInfo *win, int x, int y) { if (!HasPermission(Perm_DiskAccess) || gPluginMode) return false; if (!win->IsDocLoaded() || win->dm->engineType != Engine_PDF) return false; // Clear the last forward-search result win->fwdSearchMark.rects.Reset(); InvalidateRect(win->hwndCanvas, NULL, FALSE); // On double-clicking error message will be shown to the user // if the PDF does not have a synchronization file if (!win->pdfsync) { int err = Synchronizer::Create(win->loadedFilePath, static_cast<PdfEngine *>(win->dm->engine), &win->pdfsync); if (err == PDFSYNCERR_SYNCFILE_NOTFOUND) { // We used to warn that "No synchronization file found" at this // point if gGlobalPrefs->enableTeXEnhancements is set; we no longer // so do because a double-click has several other meanings // (selecting a word or an image, navigating quickly using links) // and showing an unrelated warning in all those cases seems wrong return false; } if (err != PDFSYNCERR_SUCCESS) { ShowNotification(win, _TR("Synchronization file cannot be opened")); return true; } gGlobalPrefs->enableTeXEnhancements = true; } int pageNo = win->dm->GetPageNoByPoint(PointI(x, y)); if (!win->dm->ValidPageNo(pageNo)) return false; PointI pt = win->dm->CvtFromScreen(PointI(x, y), pageNo).Convert<int>(); ScopedMem<WCHAR> srcfilepath; UINT line, col; int err = win->pdfsync->DocToSource(pageNo, pt, srcfilepath, &line, &col); if (err != PDFSYNCERR_SUCCESS) { ShowNotification(win, _TR("No synchronization info at this position")); return true; } WCHAR *inverseSearch = gGlobalPrefs->inverseSearchCmdLine; if (!inverseSearch) // Detect a text editor and use it as the default inverse search handler for now inverseSearch = AutoDetectInverseSearchCommands(); ScopedMem<WCHAR> cmdline; if (inverseSearch) cmdline.Set(win->pdfsync->PrepareCommandline(inverseSearch, srcfilepath, line, col)); if (!str::IsEmpty(cmdline.Get())) { // resolve relative paths with relation to SumatraPDF.exe's directory ScopedMem<WCHAR> appDir(GetExePath()); if (appDir) appDir.Set(path::GetDir(appDir)); ScopedHandle process(LaunchProcess(cmdline, appDir)); if (!process) ShowNotification(win, _TR("Cannot start inverse search command. Please check the command line in the settings.")); } else if (gGlobalPrefs->enableTeXEnhancements) ShowNotification(win, _TR("Cannot start inverse search command. Please check the command line in the settings.")); if (inverseSearch != gGlobalPrefs->inverseSearchCmdLine) free(inverseSearch); return true; }
void DumpProperties(BaseEngine *engine) { Out("\t<Properties\n"); ScopedMem<char> str; str.Set(Escape((WCHAR *)engine->FileName(), true)); Out("\t\tFilePath=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_Title))); if (str) Out("\t\tTitle=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_Subject))); if (str) Out("\t\tSubject=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_Author))); if (str) Out("\t\tAuthor=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_Copyright))); if (str) Out("\t\tCopyright=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_CreationDate))); if (str) Out("\t\tCreationDate=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_ModificationDate))); if (str) Out("\t\tModDate=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_CreatorApp))); if (str) Out("\t\tCreator=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_PdfProducer))); if (str) Out("\t\tProducer=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_PdfVersion))); if (str) Out("\t\tPdfVersion=\"%s\"\n", str.Get()); str.Set(Escape(engine->GetProperty(Prop_PdfFileStructure))); if (str) Out("\t\tPdfFileStructure=\"%s\"\n", str.Get()); if (!engine->IsPrintingAllowed()) Out("\t\tPrintingAllowed=\"no\"\n"); if (!engine->IsCopyingTextAllowed()) Out("\t\tCopyingTextAllowed=\"no\"\n"); if (engine->IsImageCollection()) Out("\t\tImageCollection=\"yes\"\n"); if (engine->PreferredLayout()) Out("\t\tPreferredLayout=\"%d\"\n", engine->PreferredLayout()); Out("\t/>\n"); ScopedMem<WCHAR> fontlist(engine->GetProperty(Prop_FontList)); if (fontlist) { WStrVec fonts; fonts.Split(fontlist, L"\n"); str.Set(Escape(fonts.Join(L"\n\t\t"))); Out("\t<FontList>\n\t\t%s\n\t</FontList>\n", str.Get()); } }
void OnMenuPrint(WindowInfo *win, bool waitForCompletion) { // we remember some printer settings per process static ScopedMem<DEVMODE> defaultDevMode; static PrintScaleAdv defaultScaleAdv = PrintScaleShrink; static bool defaultAsImage = false; static bool hasDefaults = false; if (!hasDefaults) { hasDefaults = true; defaultAsImage = gGlobalPrefs->printerDefaults.printAsImage; if (str::EqI(gGlobalPrefs->printerDefaults.printScale, "fit")) defaultScaleAdv = PrintScaleFit; else if (str::EqI(gGlobalPrefs->printerDefaults.printScale, "none")) defaultScaleAdv = PrintScaleNone; } bool printSelection = false; Vec<PRINTPAGERANGE> ranges; PRINTER_INFO_2 printerInfo = { 0 }; if (!HasPermission(Perm_PrinterAccess)) return; if (win->AsChm()) { // the Print dialog allows access to the file system, so fall back // to printing the entire document without dialog if that isn't desired bool showUI = HasPermission(Perm_DiskAccess); win->AsChm()->PrintCurrentPage(showUI); return; } if (win->AsEbook()) { // TODO: use EbookEngine for printing? return; } CrashIf(!win->AsFixed()); if (!win->AsFixed()) return; DisplayModel *dm = win->AsFixed(); #ifndef DISABLE_DOCUMENT_RESTRICTIONS if (!dm->engine()->AllowsPrinting()) return; #endif if (win->printThread) { int res = MessageBox(win->hwndFrame, _TR("Printing is still in progress. Abort and start over?"), _TR("Printing in progress."), MB_ICONEXCLAMATION | MB_YESNO | MbRtlReadingMaybe()); if (res == IDNO) return; } AbortPrinting(win); // the Print dialog allows access to the file system, so fall back // to printing the entire document without dialog if that isn't desired if (!HasPermission(Perm_DiskAccess)) { PrintFile(dm->engine()); return; } PRINTDLGEX pd; ZeroMemory(&pd, sizeof(PRINTDLGEX)); pd.lStructSize = sizeof(PRINTDLGEX); pd.hwndOwner = win->hwndFrame; pd.Flags = PD_USEDEVMODECOPIESANDCOLLATE | PD_COLLATE; if (!win->selectionOnPage) pd.Flags |= PD_NOSELECTION; pd.nCopies = 1; /* by default print all pages */ pd.nPageRanges = 1; pd.nMaxPageRanges = MAXPAGERANGES; PRINTPAGERANGE *ppr = AllocArray<PRINTPAGERANGE>(MAXPAGERANGES); pd.lpPageRanges = ppr; ppr->nFromPage = 1; ppr->nToPage = dm->PageCount(); pd.nMinPage = 1; pd.nMaxPage = dm->PageCount(); pd.nStartPage = START_PAGE_GENERAL; Print_Advanced_Data advanced(PrintRangeAll, defaultScaleAdv, defaultAsImage); ScopedMem<DLGTEMPLATE> dlgTemplate; // needed for RTL languages HPROPSHEETPAGE hPsp = CreatePrintAdvancedPropSheet(&advanced, dlgTemplate); pd.lphPropertyPages = &hPsp; pd.nPropertyPages = 1; // restore remembered settings if (defaultDevMode) { DEVMODE *p = defaultDevMode.Get(); pd.hDevMode = GlobalMemDup(p, p->dmSize + p->dmDriverExtra); } if (PrintDlgEx(&pd) != S_OK) { if (CommDlgExtendedError() != 0) { /* if PrintDlg was cancelled then CommDlgExtendedError is zero, otherwise it returns the error code, which we could look at here if we wanted. for now just warn the user that printing has stopped becasue of an error */ MessageBoxWarning(win->hwndFrame, _TR("Couldn't initialize printer"), _TR("Printing problem.")); } goto Exit; } if (pd.dwResultAction == PD_RESULT_PRINT || pd.dwResultAction == PD_RESULT_APPLY) { // remember settings for this process LPDEVMODE devMode = (LPDEVMODE)GlobalLock(pd.hDevMode); if (devMode) { defaultDevMode.Set((LPDEVMODE)memdup(devMode, devMode->dmSize + devMode->dmDriverExtra)); GlobalUnlock(pd.hDevMode); } defaultScaleAdv = advanced.scale; defaultAsImage = advanced.asImage; } if (pd.dwResultAction != PD_RESULT_PRINT) goto Exit; if (pd.Flags & PD_CURRENTPAGE) { PRINTPAGERANGE pr = { dm->CurrentPageNo(), dm->CurrentPageNo() }; ranges.Append(pr); } else if (win->selectionOnPage && (pd.Flags & PD_SELECTION)) { printSelection = true; } else if (!(pd.Flags & PD_PAGENUMS)) { PRINTPAGERANGE pr = { 1, dm->PageCount() }; ranges.Append(pr); } else { assert(pd.nPageRanges > 0); for (DWORD i = 0; i < pd.nPageRanges; i++) ranges.Append(pd.lpPageRanges[i]); } LPDEVNAMES devNames = (LPDEVNAMES)GlobalLock(pd.hDevNames); LPDEVMODE devMode = (LPDEVMODE)GlobalLock(pd.hDevMode); if (devNames) { printerInfo.pDriverName = (LPWSTR)devNames + devNames->wDriverOffset; printerInfo.pPrinterName = (LPWSTR)devNames + devNames->wDeviceOffset; printerInfo.pPortName = (LPWSTR)devNames + devNames->wOutputOffset; } PrintData *data = new PrintData(dm->engine(), &printerInfo, devMode, ranges, advanced, dm->GetRotation(), printSelection ? win->selectionOnPage : NULL); if (devNames) GlobalUnlock(pd.hDevNames); if (devMode) GlobalUnlock(pd.hDevMode); // if a file is missing and the engine can't thus be cloned, // we print using the original engine on the main thread // so that the document can't be closed and the original engine // unexpectedly deleted // TODO: instead prevent closing the document so that printing // can still happen on a separate thread and be interruptible bool failedEngineClone = dm->engine() && !data->engine; if (failedEngineClone) data->engine = dm->engine(); if (!waitForCompletion && !failedEngineClone) PrintToDeviceOnThread(win, data); else { PrintToDevice(*data); if (failedEngineClone) data->engine = NULL; delete data; } Exit: free(ppr); GlobalFree(pd.hDevNames); GlobalFree(pd.hDevMode); }