| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/renderer/printing/print_web_view_helper.h" | 5 #include "chrome/renderer/printing/print_web_view_helper.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/auto_reset.h" | 9 #include "base/auto_reset.h" |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 60 PREVIEW_EVENT_MAX, | 60 PREVIEW_EVENT_MAX, |
| 61 }; | 61 }; |
| 62 | 62 |
| 63 const double kMinDpi = 1.0; | 63 const double kMinDpi = 1.0; |
| 64 | 64 |
| 65 const char kPageLoadScriptFormat[] = | 65 const char kPageLoadScriptFormat[] = |
| 66 "document.open(); document.write(%s); document.close();"; | 66 "document.open(); document.write(%s); document.close();"; |
| 67 | 67 |
| 68 const char kPageSetupScriptFormat[] = "setup(%s);"; | 68 const char kPageSetupScriptFormat[] = "setup(%s);"; |
| 69 | 69 |
| 70 #if defined(ENABLE_FULL_PRINTING) | |
| 71 bool g_is_preview_enabled_ = true; | |
| 72 #else | |
| 73 bool g_is_preview_enabled_ = false; | |
| 74 #endif | |
| 75 | |
| 76 void ExecuteScript(blink::WebFrame* frame, | 70 void ExecuteScript(blink::WebFrame* frame, |
| 77 const char* script_format, | 71 const char* script_format, |
| 78 const base::Value& parameters) { | 72 const base::Value& parameters) { |
| 79 std::string json; | 73 std::string json; |
| 80 base::JSONWriter::Write(¶meters, &json); | 74 base::JSONWriter::Write(¶meters, &json); |
| 81 std::string script = base::StringPrintf(script_format, json.c_str()); | 75 std::string script = base::StringPrintf(script_format, json.c_str()); |
| 82 frame->executeScript(blink::WebString(base::UTF8ToUTF16(script))); | 76 frame->executeScript(blink::WebString(base::UTF8ToUTF16(script))); |
| 83 } | 77 } |
| 84 | 78 |
| 85 int GetDPI(const PrintMsg_Print_Params* print_params) { | 79 int GetDPI(const PrintMsg_Print_Params* print_params) { |
| (...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 } | 392 } |
| 399 | 393 |
| 400 if (fit_to_page) { | 394 if (fit_to_page) { |
| 401 double factor = FitPrintParamsToPage(params, &result_params); | 395 double factor = FitPrintParamsToPage(params, &result_params); |
| 402 if (scale_factor) | 396 if (scale_factor) |
| 403 *scale_factor = factor; | 397 *scale_factor = factor; |
| 404 } | 398 } |
| 405 return result_params; | 399 return result_params; |
| 406 } | 400 } |
| 407 | 401 |
| 402 bool IsPrintPreviewEnabled() { |
| 403 #if defined(ENABLE_FULL_PRINTING) |
| 404 return !CommandLine::ForCurrentProcess()->HasSwitch( |
| 405 switches::kDisablePrintPreview); |
| 406 #else |
| 407 return false; |
| 408 #endif |
| 409 } |
| 410 |
| 408 } // namespace | 411 } // namespace |
| 409 | 412 |
| 410 FrameReference::FrameReference(blink::WebLocalFrame* frame) { | 413 FrameReference::FrameReference(blink::WebLocalFrame* frame) { |
| 411 Reset(frame); | 414 Reset(frame); |
| 412 } | 415 } |
| 413 | 416 |
| 414 FrameReference::FrameReference() { | 417 FrameReference::FrameReference() { |
| 415 Reset(NULL); | 418 Reset(NULL); |
| 416 } | 419 } |
| 417 | 420 |
| (...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 768 } | 771 } |
| 769 } | 772 } |
| 770 frame_.Reset(NULL); | 773 frame_.Reset(NULL); |
| 771 on_ready_.Reset(); | 774 on_ready_.Reset(); |
| 772 } | 775 } |
| 773 | 776 |
| 774 PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view) | 777 PrintWebViewHelper::PrintWebViewHelper(content::RenderView* render_view) |
| 775 : content::RenderViewObserver(render_view), | 778 : content::RenderViewObserver(render_view), |
| 776 content::RenderViewObserverTracker<PrintWebViewHelper>(render_view), | 779 content::RenderViewObserverTracker<PrintWebViewHelper>(render_view), |
| 777 reset_prep_frame_view_(false), | 780 reset_prep_frame_view_(false), |
| 781 is_preview_enabled_(IsPrintPreviewEnabled()), |
| 778 is_print_ready_metafile_sent_(false), | 782 is_print_ready_metafile_sent_(false), |
| 779 ignore_css_margins_(false), | 783 ignore_css_margins_(false), |
| 780 is_scripted_printing_blocked_(false), | 784 is_scripted_printing_blocked_(false), |
| 781 notify_browser_of_print_failure_(true), | 785 notify_browser_of_print_failure_(true), |
| 782 print_for_preview_(false), | 786 print_for_preview_(false), |
| 783 print_node_in_progress_(false), | 787 print_node_in_progress_(false), |
| 784 is_loading_(false), | 788 is_loading_(false), |
| 785 is_scripted_preview_delayed_(false), | 789 is_scripted_preview_delayed_(false), |
| 786 weak_ptr_factory_(this) { | 790 weak_ptr_factory_(this) { |
| 787 } | 791 } |
| 788 | 792 |
| 789 PrintWebViewHelper::~PrintWebViewHelper() {} | 793 PrintWebViewHelper::~PrintWebViewHelper() {} |
| 790 | 794 |
| 791 // static | |
| 792 void PrintWebViewHelper::DisablePreview() { | |
| 793 g_is_preview_enabled_ = false; | |
| 794 } | |
| 795 | |
| 796 bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed( | 795 bool PrintWebViewHelper::IsScriptInitiatedPrintAllowed( |
| 797 blink::WebFrame* frame, bool user_initiated) { | 796 blink::WebFrame* frame, bool user_initiated) { |
| 798 #if defined(OS_ANDROID) | 797 #if defined(OS_ANDROID) |
| 799 return false; | 798 return false; |
| 800 #endif // defined(OS_ANDROID) | 799 #endif // defined(OS_ANDROID) |
| 801 // If preview is enabled, then the print dialog is tab modal, and the user | 800 // If preview is enabled, then the print dialog is tab modal, and the user |
| 802 // can always close the tab on a mis-behaving page (the system print dialog | 801 // can always close the tab on a mis-behaving page (the system print dialog |
| 803 // is app modal). If the print was initiated through user action, don't | 802 // is app modal). If the print was initiated through user action, don't |
| 804 // throttle. Or, if the command line flag to skip throttling has been set. | 803 // throttle. Or, if the command line flag to skip throttling has been set. |
| 805 return !is_scripted_printing_blocked_ && | 804 return !is_scripted_printing_blocked_ && |
| 806 (user_initiated || g_is_preview_enabled_ || | 805 (user_initiated || is_preview_enabled_ || |
| 807 scripting_throttler_.IsAllowed(frame)); | 806 scripting_throttler_.IsAllowed(frame)); |
| 808 } | 807 } |
| 809 | 808 |
| 810 void PrintWebViewHelper::DidStartLoading() { | 809 void PrintWebViewHelper::DidStartLoading() { |
| 811 is_loading_ = true; | 810 is_loading_ = true; |
| 812 } | 811 } |
| 813 | 812 |
| 814 void PrintWebViewHelper::DidStopLoading() { | 813 void PrintWebViewHelper::DidStopLoading() { |
| 815 is_loading_ = false; | 814 is_loading_ = false; |
| 816 if (!on_stop_loading_closure_.is_null()) { | 815 if (!on_stop_loading_closure_.is_null()) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 827 // Allow Prerendering to cancel this print request if necessary. | 826 // Allow Prerendering to cancel this print request if necessary. |
| 828 if (prerender::PrerenderHelper::IsPrerendering( | 827 if (prerender::PrerenderHelper::IsPrerendering( |
| 829 render_view()->GetMainRenderFrame())) { | 828 render_view()->GetMainRenderFrame())) { |
| 830 Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id())); | 829 Send(new ChromeViewHostMsg_CancelPrerenderForPrinting(routing_id())); |
| 831 return; | 830 return; |
| 832 } | 831 } |
| 833 | 832 |
| 834 if (!IsScriptInitiatedPrintAllowed(frame, user_initiated)) | 833 if (!IsScriptInitiatedPrintAllowed(frame, user_initiated)) |
| 835 return; | 834 return; |
| 836 | 835 |
| 837 if (!g_is_preview_enabled_) { | 836 if (!is_preview_enabled_) { |
| 838 Print(frame, blink::WebNode()); | 837 Print(frame, blink::WebNode()); |
| 839 } else { | 838 } else { |
| 840 print_preview_context_.InitWithFrame(frame); | 839 print_preview_context_.InitWithFrame(frame); |
| 841 RequestPrintPreview(PRINT_PREVIEW_SCRIPTED); | 840 RequestPrintPreview(PRINT_PREVIEW_SCRIPTED); |
| 842 } | 841 } |
| 843 } | 842 } |
| 844 | 843 |
| 845 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) { | 844 bool PrintWebViewHelper::OnMessageReceived(const IPC::Message& message) { |
| 846 bool handled = true; | 845 bool handled = true; |
| 847 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message) | 846 IPC_BEGIN_MESSAGE_MAP(PrintWebViewHelper, message) |
| 848 #if !defined(DISABLE_BASIC_PRINTING) | 847 #if !defined(DISABLE_BASIC_PRINTING) |
| 849 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages) | 848 IPC_MESSAGE_HANDLER(PrintMsg_PrintPages, OnPrintPages) |
| 850 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog) | 849 IPC_MESSAGE_HANDLER(PrintMsg_PrintForSystemDialog, OnPrintForSystemDialog) |
| 851 #endif // !DISABLE_BASIC_PRINTING | 850 #endif // !DISABLE_BASIC_PRINTING |
| 852 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview) | 851 IPC_MESSAGE_HANDLER(PrintMsg_InitiatePrintPreview, OnInitiatePrintPreview) |
| 853 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview) | 852 IPC_MESSAGE_HANDLER(PrintMsg_PrintPreview, OnPrintPreview) |
| 854 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview) | 853 IPC_MESSAGE_HANDLER(PrintMsg_PrintForPrintPreview, OnPrintForPrintPreview) |
| 855 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone) | 854 IPC_MESSAGE_HANDLER(PrintMsg_PrintingDone, OnPrintingDone) |
| 856 IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked, | 855 IPC_MESSAGE_HANDLER(PrintMsg_SetScriptedPrintingBlocked, |
| 857 SetScriptedPrintBlocked) | 856 SetScriptedPrintBlocked) |
| 858 IPC_MESSAGE_UNHANDLED(handled = false) | 857 IPC_MESSAGE_UNHANDLED(handled = false) |
| 859 IPC_END_MESSAGE_MAP() | 858 IPC_END_MESSAGE_MAP() |
| 860 return handled; | 859 return handled; |
| 861 } | 860 } |
| 862 | 861 |
| 863 void PrintWebViewHelper::OnPrintForPrintPreview( | 862 void PrintWebViewHelper::OnPrintForPrintPreview( |
| 864 const base::DictionaryValue& job_settings) { | 863 const base::DictionaryValue& job_settings) { |
| 864 DCHECK(is_preview_enabled_); |
| 865 // If still not finished with earlier print request simply ignore. | 865 // If still not finished with earlier print request simply ignore. |
| 866 if (prep_frame_view_) | 866 if (prep_frame_view_) |
| 867 return; | 867 return; |
| 868 | 868 |
| 869 if (!render_view()->GetWebView()) | 869 if (!render_view()->GetWebView()) |
| 870 return; | 870 return; |
| 871 blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); | 871 blink::WebFrame* main_frame = render_view()->GetWebView()->mainFrame(); |
| 872 if (!main_frame) | 872 if (!main_frame) |
| 873 return; | 873 return; |
| 874 | 874 |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 989 | 989 |
| 990 bool PrintWebViewHelper::IsPrintToPdfRequested( | 990 bool PrintWebViewHelper::IsPrintToPdfRequested( |
| 991 const base::DictionaryValue& job_settings) { | 991 const base::DictionaryValue& job_settings) { |
| 992 bool print_to_pdf = false; | 992 bool print_to_pdf = false; |
| 993 if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf)) | 993 if (!job_settings.GetBoolean(kSettingPrintToPDF, &print_to_pdf)) |
| 994 NOTREACHED(); | 994 NOTREACHED(); |
| 995 return print_to_pdf; | 995 return print_to_pdf; |
| 996 } | 996 } |
| 997 | 997 |
| 998 void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) { | 998 void PrintWebViewHelper::OnPrintPreview(const base::DictionaryValue& settings) { |
| 999 DCHECK(is_preview_enabled_); |
| 999 print_preview_context_.OnPrintPreview(); | 1000 print_preview_context_.OnPrintPreview(); |
| 1000 | 1001 |
| 1001 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent", | 1002 UMA_HISTOGRAM_ENUMERATION("PrintPreview.PreviewEvent", |
| 1002 PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX); | 1003 PREVIEW_EVENT_REQUESTED, PREVIEW_EVENT_MAX); |
| 1003 | 1004 |
| 1004 if (!print_preview_context_.source_frame()) { | 1005 if (!print_preview_context_.source_frame()) { |
| 1005 DidFinishPrinting(FAIL_PREVIEW); | 1006 DidFinishPrinting(FAIL_PREVIEW); |
| 1006 return; | 1007 return; |
| 1007 } | 1008 } |
| 1008 | 1009 |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1191 if (!success) | 1192 if (!success) |
| 1192 LOG(ERROR) << "Failure in OnPrintingDone"; | 1193 LOG(ERROR) << "Failure in OnPrintingDone"; |
| 1193 DidFinishPrinting(success ? OK : FAIL_PRINT); | 1194 DidFinishPrinting(success ? OK : FAIL_PRINT); |
| 1194 } | 1195 } |
| 1195 | 1196 |
| 1196 void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked) { | 1197 void PrintWebViewHelper::SetScriptedPrintBlocked(bool blocked) { |
| 1197 is_scripted_printing_blocked_ = blocked; | 1198 is_scripted_printing_blocked_ = blocked; |
| 1198 } | 1199 } |
| 1199 | 1200 |
| 1200 void PrintWebViewHelper::OnInitiatePrintPreview(bool selection_only) { | 1201 void PrintWebViewHelper::OnInitiatePrintPreview(bool selection_only) { |
| 1202 DCHECK(is_preview_enabled_); |
| 1201 blink::WebLocalFrame* frame = NULL; | 1203 blink::WebLocalFrame* frame = NULL; |
| 1202 GetPrintFrame(&frame); | 1204 GetPrintFrame(&frame); |
| 1203 DCHECK(frame); | 1205 DCHECK(frame); |
| 1204 print_preview_context_.InitWithFrame(frame); | 1206 print_preview_context_.InitWithFrame(frame); |
| 1205 RequestPrintPreview(selection_only ? | 1207 RequestPrintPreview(selection_only ? |
| 1206 PRINT_PREVIEW_USER_INITIATED_SELECTION : | 1208 PRINT_PREVIEW_USER_INITIATED_SELECTION : |
| 1207 PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME); | 1209 PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME); |
| 1208 } | 1210 } |
| 1209 | 1211 |
| 1210 bool PrintWebViewHelper::IsPrintingEnabled() { | 1212 bool PrintWebViewHelper::IsPrintingEnabled() { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 1224 // This can happen as a result of processing sync messages when printing | 1226 // This can happen as a result of processing sync messages when printing |
| 1225 // from ppapi plugins. It's a rare case, so its OK to just fail here. | 1227 // from ppapi plugins. It's a rare case, so its OK to just fail here. |
| 1226 // See http://crbug.com/159165. | 1228 // See http://crbug.com/159165. |
| 1227 return; | 1229 return; |
| 1228 } | 1230 } |
| 1229 | 1231 |
| 1230 print_node_in_progress_ = true; | 1232 print_node_in_progress_ = true; |
| 1231 | 1233 |
| 1232 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets | 1234 // Make a copy of the node, in case RenderView::OnContextMenuClosed resets |
| 1233 // its |context_menu_node_|. | 1235 // its |context_menu_node_|. |
| 1234 if (!g_is_preview_enabled_) { | 1236 if (!is_preview_enabled_) { |
| 1235 blink::WebNode duplicate_node(node); | 1237 blink::WebNode duplicate_node(node); |
| 1236 Print(duplicate_node.document().frame(), duplicate_node); | 1238 Print(duplicate_node.document().frame(), duplicate_node); |
| 1237 } else { | 1239 } else { |
| 1238 print_preview_context_.InitWithNode(node); | 1240 print_preview_context_.InitWithNode(node); |
| 1239 RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE); | 1241 RequestPrintPreview(PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE); |
| 1240 } | 1242 } |
| 1241 | 1243 |
| 1242 print_node_in_progress_ = false; | 1244 print_node_in_progress_ = false; |
| 1243 } | 1245 } |
| 1244 | 1246 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1287 break; | 1289 break; |
| 1288 | 1290 |
| 1289 case FAIL_PRINT: | 1291 case FAIL_PRINT: |
| 1290 if (notify_browser_of_print_failure_ && print_pages_params_) { | 1292 if (notify_browser_of_print_failure_ && print_pages_params_) { |
| 1291 int cookie = print_pages_params_->params.document_cookie; | 1293 int cookie = print_pages_params_->params.document_cookie; |
| 1292 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie)); | 1294 Send(new PrintHostMsg_PrintingFailed(routing_id(), cookie)); |
| 1293 } | 1295 } |
| 1294 break; | 1296 break; |
| 1295 | 1297 |
| 1296 case FAIL_PREVIEW: | 1298 case FAIL_PREVIEW: |
| 1299 DCHECK(is_preview_enabled_); |
| 1297 int cookie = print_pages_params_ ? | 1300 int cookie = print_pages_params_ ? |
| 1298 print_pages_params_->params.document_cookie : 0; | 1301 print_pages_params_->params.document_cookie : 0; |
| 1299 if (notify_browser_of_print_failure_) { | 1302 if (notify_browser_of_print_failure_) { |
| 1300 LOG(ERROR) << "CreatePreviewDocument failed"; | 1303 LOG(ERROR) << "CreatePreviewDocument failed"; |
| 1301 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie)); | 1304 Send(new PrintHostMsg_PrintPreviewFailed(routing_id(), cookie)); |
| 1302 } else { | 1305 } else { |
| 1303 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie)); | 1306 Send(new PrintHostMsg_PrintPreviewCancelled(routing_id(), cookie)); |
| 1304 } | 1307 } |
| 1305 print_preview_context_.Failed(notify_browser_of_print_failure_); | 1308 print_preview_context_.Failed(notify_browser_of_print_failure_); |
| 1306 break; | 1309 break; |
| (...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1452 const blink::WebNode& source_node = print_preview_context_.source_node(); | 1455 const blink::WebNode& source_node = print_preview_context_.source_node(); |
| 1453 | 1456 |
| 1454 params.is_scaling_disabled = | 1457 params.is_scaling_disabled = |
| 1455 source_frame->isPrintScalingDisabledForPlugin(source_node); | 1458 source_frame->isPrintScalingDisabledForPlugin(source_node); |
| 1456 } | 1459 } |
| 1457 | 1460 |
| 1458 bool PrintWebViewHelper::UpdatePrintSettings( | 1461 bool PrintWebViewHelper::UpdatePrintSettings( |
| 1459 blink::WebLocalFrame* frame, | 1462 blink::WebLocalFrame* frame, |
| 1460 const blink::WebNode& node, | 1463 const blink::WebNode& node, |
| 1461 const base::DictionaryValue& passed_job_settings) { | 1464 const base::DictionaryValue& passed_job_settings) { |
| 1465 DCHECK(is_preview_enabled_); |
| 1462 const base::DictionaryValue* job_settings = &passed_job_settings; | 1466 const base::DictionaryValue* job_settings = &passed_job_settings; |
| 1463 base::DictionaryValue modified_job_settings; | 1467 base::DictionaryValue modified_job_settings; |
| 1464 if (job_settings->empty()) { | 1468 if (job_settings->empty()) { |
| 1465 if (!print_for_preview_) | 1469 if (!print_for_preview_) |
| 1466 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING); | 1470 print_preview_context_.set_error(PREVIEW_ERROR_BAD_SETTING); |
| 1467 return false; | 1471 return false; |
| 1468 } | 1472 } |
| 1469 | 1473 |
| 1470 bool source_is_html = true; | 1474 bool source_is_html = true; |
| 1471 if (print_for_preview_) { | 1475 if (print_for_preview_) { |
| (...skipping 545 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2017 blink::WebConsoleMessage::LevelWarning, message)); | 2021 blink::WebConsoleMessage::LevelWarning, message)); |
| 2018 return false; | 2022 return false; |
| 2019 } | 2023 } |
| 2020 | 2024 |
| 2021 void PrintWebViewHelper::ScriptingThrottler::Reset() { | 2025 void PrintWebViewHelper::ScriptingThrottler::Reset() { |
| 2022 // Reset counter on successful print. | 2026 // Reset counter on successful print. |
| 2023 count_ = 0; | 2027 count_ = 0; |
| 2024 } | 2028 } |
| 2025 | 2029 |
| 2026 } // namespace printing | 2030 } // namespace printing |
| OLD | NEW |