#include "chrome/browser/printing/print_job.h"
#include <memory>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/metrics/histogram_functions.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/printing/print_job_manager.h"
#include "chrome/browser/printing/print_job_worker.h"
#include "chrome/browser/printing/printer_query.h"
#include "components/enterprise/buildflags/buildflags.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
#include "printing/mojom/print.mojom.h"
#include "printing/printed_document.h"
#if BUILDFLAG(IS_WIN)
#include <optional>
#include "base/command_line.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/pdf/pdf_pref_names.h"
#include "chrome/browser/printing/pdf_to_emf_converter.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_service.h"
#include "printing/backend/win_helper.h"
#include "printing/page_number.h"
#include "printing/pdf_render_settings.h"
#include "printing/printed_page_win.h"
#include "printing/printing_features.h"
#include "url/gurl.h"
#endif
namespace printing {
namespace {
void HoldRefCallback(scoped_refptr<PrintJob> job, base::OnceClosure callback) { … }
#if BUILDFLAG(IS_WIN)
enum class PrintPostScriptMode {
kDefault = 0,
kType42 = 1,
kMaxValue = kType42,
};
enum class PrintRasterizationMode {
kFull = 0,
kFast = 1,
kMaxValue = kFast,
};
bool PrintWithPostScriptType42Fonts(PrefService* prefs) {
if (prefs && prefs->IsManagedPreference(prefs::kPrintPostScriptMode)) {
int value = prefs->GetInteger(prefs::kPrintPostScriptMode);
return value == static_cast<int>(PrintPostScriptMode::kType42);
}
return base::FeatureList::IsEnabled(
features::kPrintWithPostScriptType42Fonts);
}
bool PrintWithReducedRasterization(PrefService* prefs) {
if (prefs && prefs->IsManagedPreference(prefs::kPrintRasterizationMode)) {
int value = prefs->GetInteger(prefs::kPrintRasterizationMode);
return value == static_cast<int>(PrintRasterizationMode::kFast);
}
return base::FeatureList::IsEnabled(features::kPrintWithReducedRasterization);
}
PrefService* GetPrefsForWebContents(content::WebContents* web_contents) {
content::BrowserContext* context =
web_contents ? web_contents->GetBrowserContext() : nullptr;
return context ? Profile::FromBrowserContext(context)->GetPrefs() : nullptr;
}
content::WebContents* GetWebContents(content::GlobalRenderFrameHostId rfh_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto* rfh = content::RenderFrameHost::FromID(rfh_id);
return rfh ? content::WebContents::FromRenderFrameHost(rfh) : nullptr;
}
#endif
}
PrintJob::PrintJob(PrintJobManager* print_job_manager)
: … { … }
PrintJob::PrintJob() = default;
PrintJob::~PrintJob() { … }
void PrintJob::Initialize(std::unique_ptr<PrinterQuery> query,
const std::u16string& name,
uint32_t page_count) { … }
#if BUILDFLAG(IS_WIN)
std::vector<uint32_t> PrintJob::GetFullPageMapping(
const std::vector<uint32_t>& pages,
uint32_t total_page_count) {
std::vector<uint32_t> mapping(total_page_count, kInvalidPageIndex);
for (uint32_t page_index : pages) {
if (page_index < total_page_count) {
mapping[page_index] = page_index;
}
}
return mapping;
}
void PrintJob::StartConversionToNativeFormat(
scoped_refptr<base::RefCountedMemory> print_data,
const gfx::Size& page_size,
const gfx::Rect& content_area,
const gfx::Point& physical_offsets,
const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (PrintedDocument::HasDebugDumpPath())
document()->DebugDumpData(print_data.get(), FILE_PATH_LITERAL(".pdf"));
const PrintSettings& settings = document()->settings();
if (settings.printer_language_is_textonly()) {
StartPdfToTextConversion(print_data, page_size, url);
} else if (settings.printer_language_is_ps2() ||
settings.printer_language_is_ps3()) {
StartPdfToPostScriptConversion(print_data, content_area, physical_offsets,
settings.printer_language_is_ps2(), url);
} else {
StartPdfToEmfConversion(print_data, page_size, content_area, url);
}
document()->SetConvertingPdf();
}
void PrintJob::ResetPageMapping() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
pdf_page_mapping_ =
GetFullPageMapping(pdf_page_mapping_, document_->page_count());
}
#endif
void PrintJob::StartPrinting() { … }
void PrintJob::Stop() { … }
void PrintJob::Cancel() { … }
#if BUILDFLAG(ENTERPRISE_CONTENT_ANALYSIS)
void PrintJob::CleanupAfterContentAnalysisDenial() { … }
#endif
bool PrintJob::FlushJob(base::TimeDelta timeout) { … }
bool PrintJob::is_job_pending() const { … }
PrintedDocument* PrintJob::document() const { … }
const PrintSettings& PrintJob::settings() const { … }
#if BUILDFLAG(IS_CHROMEOS)
void PrintJob::SetSource(PrintJob::Source source,
const std::string& source_id) {
source_ = source;
source_id_ = source_id;
}
PrintJob::Source PrintJob::source() const {
return source_;
}
const std::string& PrintJob::source_id() const {
return source_id_;
}
#endif
#if BUILDFLAG(IS_WIN)
class PrintJob::PdfConversionState {
public:
PdfConversionState(const gfx::Size& page_size,
const gfx::Rect& content_area,
const std::optional<bool>& use_skia,
const GURL& url)
: page_size_(page_size),
content_area_(content_area),
use_skia_(use_skia),
url_(url) {}
void Start(scoped_refptr<base::RefCountedMemory> data,
const PdfRenderSettings& conversion_settings,
PdfConverter::StartCallback start_callback) {
converter_ = PdfConverter::StartPdfConverter(
data, conversion_settings, use_skia_, url_, std::move(start_callback));
}
void GetMorePages(PdfConverter::GetPageCallback get_page_callback) {
const int kMaxNumberOfTempFilesPerDocument = 3;
while (pages_in_progress_ < kMaxNumberOfTempFilesPerDocument &&
current_page_index_ < page_count_) {
++pages_in_progress_;
converter_->GetPage(current_page_index_++, get_page_callback);
}
}
void OnPageProcessed(PdfConverter::GetPageCallback get_page_callback) {
--pages_in_progress_;
GetMorePages(get_page_callback);
if (!pages_in_progress_ && current_page_index_ >= page_count_)
converter_.reset();
}
void set_page_count(uint32_t page_count) { page_count_ = page_count; }
const gfx::Size& page_size() const { return page_size_; }
const gfx::Rect& content_area() const { return content_area_; }
private:
uint32_t page_count_ = 0;
uint32_t current_page_index_ = 0;
int pages_in_progress_ = 0;
const gfx::Size page_size_;
const gfx::Rect content_area_;
const std::optional<bool> use_skia_;
const GURL url_;
std::unique_ptr<PdfConverter> converter_;
};
void PrintJob::StartPdfToEmfConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Size& page_size,
const gfx::Rect& content_area,
const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!pdf_conversion_state_);
pdf_conversion_state_ = std::make_unique<PdfConversionState>(
page_size, content_area, use_skia_, url);
const PrintSettings& settings = document()->settings();
PrefService* prefs = GetPrefsForWebContents(GetWebContents(rfh_id_));
bool print_with_reduced_rasterization = PrintWithReducedRasterization(prefs);
using RenderMode = PdfRenderSettings::Mode;
RenderMode mode = print_with_reduced_rasterization
? RenderMode::EMF_WITH_REDUCED_RASTERIZATION
: RenderMode::NORMAL;
PdfRenderSettings render_settings(
content_area, gfx::Point(0, 0), settings.dpi_size(),
true, settings.color() == mojom::ColorModel::kColor, mode);
pdf_conversion_state_->Start(
bytes, render_settings,
base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
}
void PrintJob::OnPdfConversionStarted(uint32_t page_count) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (page_count <= 0) {
scoped_refptr<PrintJob> handle(this);
pdf_conversion_state_.reset();
Cancel();
return;
}
pdf_conversion_state_->set_page_count(page_count);
pdf_conversion_state_->GetMorePages(
base::BindRepeating(&PrintJob::OnPdfPageConverted, this));
}
void PrintJob::OnPdfPageConverted(uint32_t page_index,
float scale_factor,
std::unique_ptr<MetafilePlayer> metafile) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(pdf_conversion_state_);
if (!document_ || !metafile || page_index == kInvalidPageIndex ||
page_index >= pdf_page_mapping_.size()) {
scoped_refptr<PrintJob> handle(this);
pdf_conversion_state_.reset();
Cancel();
return;
}
if (pdf_page_mapping_[page_index] != kInvalidPageIndex) {
document_->SetPage(pdf_page_mapping_[page_index], std::move(metafile),
scale_factor, pdf_conversion_state_->page_size(),
pdf_conversion_state_->content_area());
}
pdf_conversion_state_->GetMorePages(
base::BindRepeating(&PrintJob::OnPdfPageConverted, this));
}
void PrintJob::StartPdfToTextConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Size& page_size,
const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!pdf_conversion_state_);
pdf_conversion_state_ = std::make_unique<PdfConversionState>(
gfx::Size(), gfx::Rect(), use_skia_, url);
gfx::Rect page_area = gfx::Rect(0, 0, page_size.width(), page_size.height());
const PrintSettings& settings = document()->settings();
PdfRenderSettings render_settings(
page_area, gfx::Point(0, 0), settings.dpi_size(),
true,
true, PdfRenderSettings::Mode::TEXTONLY);
pdf_conversion_state_->Start(
bytes, render_settings,
base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
}
void PrintJob::StartPdfToPostScriptConversion(
scoped_refptr<base::RefCountedMemory> bytes,
const gfx::Rect& content_area,
const gfx::Point& physical_offsets,
bool ps_level2,
const GURL& url) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!pdf_conversion_state_);
pdf_conversion_state_ = std::make_unique<PdfConversionState>(
gfx::Size(), gfx::Rect(), use_skia_, url);
const PrintSettings& settings = document()->settings();
PdfRenderSettings::Mode mode;
if (ps_level2) {
mode = PdfRenderSettings::Mode::POSTSCRIPT_LEVEL2;
} else {
PrefService* prefs = GetPrefsForWebContents(GetWebContents(rfh_id_));
mode = PrintWithPostScriptType42Fonts(prefs)
? PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3_WITH_TYPE42_FONTS
: PdfRenderSettings::Mode::POSTSCRIPT_LEVEL3;
}
PdfRenderSettings render_settings(
content_area, physical_offsets, settings.dpi_size(),
true, settings.color() == mojom::ColorModel::kColor, mode);
pdf_conversion_state_->Start(
bytes, render_settings,
base::BindOnce(&PrintJob::OnPdfConversionStarted, this));
}
#endif
void PrintJob::UpdatePrintedDocument(
scoped_refptr<PrintedDocument> new_document) { … }
void PrintJob::ClearPrintedDocument() { … }
void PrintJob::SyncPrintedDocumentToWorker() { … }
#if BUILDFLAG(IS_WIN)
void PrintJob::OnPageDone(PrintedPage* page) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (pdf_conversion_state_) {
pdf_conversion_state_->OnPageProcessed(
base::BindRepeating(&PrintJob::OnPdfPageConverted, this));
}
document_->RemovePage(page);
}
#endif
void PrintJob::OnFailed() { … }
void PrintJob::OnDocDone(int job_id, PrintedDocument* document) { … }
void PrintJob::OnDocumentDone() { … }
void PrintJob::ControlledWorkerShutdown() { … }
bool PrintJob::PostTask(const base::Location& from_here,
base::OnceClosure task) { … }
void PrintJob::HoldUntilStopIsCalled() { … }
void PrintJob::set_job_pending_for_testing(bool pending) { … }
void PrintJob::AddObserver(Observer& observer) { … }
void PrintJob::RemoveObserver(Observer& observer) { … }
}