PrintTarget.cpp (5963B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "PrintTarget.h" 7 8 #include "cairo.h" 9 #ifdef CAIRO_HAS_QUARTZ_SURFACE 10 # include "cairo-quartz.h" 11 #endif 12 #ifdef CAIRO_HAS_WIN32_SURFACE 13 # include "cairo-win32.h" 14 #endif 15 #include "mozilla/gfx/2D.h" 16 #include "mozilla/gfx/HelpersCairo.h" 17 #include "mozilla/gfx/Logging.h" 18 #include "mozilla/StaticPrefs_gfx.h" 19 #include "nsReadableUtils.h" 20 #include "nsString.h" 21 #include "nsUTF8Utils.h" 22 23 // IPP spec disallow the job-name which is over 255 characters. 24 // RFC: https://tools.ietf.org/html/rfc2911#section-4.1.2 25 #define IPP_JOB_NAME_LIMIT_LENGTH 255 26 27 namespace mozilla::gfx { 28 29 PrintTarget::PrintTarget(cairo_surface_t* aCairoSurface, const IntSize& aSize) 30 : mCairoSurface(aCairoSurface), 31 mSize(aSize), 32 mIsFinished(false) 33 #ifdef DEBUG 34 , 35 mHasActivePage(false) 36 #endif 37 38 { 39 #if 0 40 // aCairoSurface is null when our PrintTargetThebes subclass's ctor calls us. 41 // Once PrintTargetThebes is removed, enable this assertion. 42 MOZ_ASSERT(aCairoSurface && !cairo_surface_status(aCairoSurface), 43 "CreateOrNull factory methods should not call us without a " 44 "valid cairo_surface_t*"); 45 #endif 46 47 // CreateOrNull factory methods hand over ownership of aCairoSurface, 48 // so we don't call cairo_surface_reference(aSurface) here. 49 50 // This code was copied from gfxASurface::Init: 51 if (mCairoSurface && 52 cairo_surface_get_content(mCairoSurface) != CAIRO_CONTENT_COLOR) { 53 cairo_surface_set_subpixel_antialiasing( 54 mCairoSurface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED); 55 } 56 } 57 58 PrintTarget::~PrintTarget() { 59 // null surfaces are allowed here 60 cairo_surface_destroy(mCairoSurface); 61 mCairoSurface = nullptr; 62 } 63 64 already_AddRefed<DrawTarget> PrintTarget::MakeDrawTarget( 65 const IntSize& aSize, DrawEventRecorder* aRecorder) { 66 MOZ_ASSERT(mCairoSurface, 67 "We shouldn't have been constructed without a cairo surface"); 68 69 // This should not be called outside of BeginPage()/EndPage() calls since 70 // some backends can only provide a valid DrawTarget at that time. 71 MOZ_ASSERT(mHasActivePage, "We can't guarantee a valid DrawTarget"); 72 73 if (cairo_surface_status(mCairoSurface)) { 74 return nullptr; 75 } 76 77 // Note than aSize may not be the same as mSize (the size of mCairoSurface). 78 // See the comments in our header. If the sizes are different a clip will 79 // be applied to mCairoSurface. 80 RefPtr<DrawTarget> dt = 81 Factory::CreateDrawTargetForCairoSurface(mCairoSurface, aSize); 82 if (!dt || !dt->IsValid()) { 83 return nullptr; 84 } 85 86 if (aRecorder) { 87 dt = CreateRecordingDrawTarget(aRecorder, dt); 88 if (!dt || !dt->IsValid()) { 89 return nullptr; 90 } 91 } 92 93 return dt.forget(); 94 } 95 96 already_AddRefed<DrawTarget> PrintTarget::GetReferenceDrawTarget() { 97 if (!mRefDT) { 98 const IntSize size(1, 1); 99 100 cairo_surface_t* similar; 101 switch (cairo_surface_get_type(mCairoSurface)) { 102 #ifdef CAIRO_HAS_WIN32_SURFACE 103 case CAIRO_SURFACE_TYPE_WIN32: 104 similar = cairo_win32_surface_create_with_dib( 105 CairoContentToCairoFormat(cairo_surface_get_content(mCairoSurface)), 106 size.width, size.height); 107 break; 108 #endif 109 #ifdef CAIRO_HAS_QUARTZ_SURFACE 110 case CAIRO_SURFACE_TYPE_QUARTZ: 111 if (StaticPrefs::gfx_cairo_quartz_cg_layer_enabled()) { 112 similar = cairo_quartz_surface_create_cg_layer( 113 mCairoSurface, cairo_surface_get_content(mCairoSurface), 114 size.width, size.height); 115 break; 116 } 117 [[fallthrough]]; 118 #endif 119 default: 120 similar = cairo_surface_create_similar( 121 mCairoSurface, cairo_surface_get_content(mCairoSurface), size.width, 122 size.height); 123 break; 124 } 125 126 if (cairo_surface_status(similar)) { 127 return nullptr; 128 } 129 130 RefPtr<DrawTarget> dt = 131 Factory::CreateDrawTargetForCairoSurface(similar, size); 132 133 // The DT addrefs the surface, so we need drop our own reference to it: 134 cairo_surface_destroy(similar); 135 136 if (!dt || !dt->IsValid()) { 137 return nullptr; 138 } 139 mRefDT = std::move(dt); 140 } 141 142 return do_AddRef(mRefDT); 143 } 144 145 /* static */ 146 void PrintTarget::AdjustPrintJobNameForIPP(const nsAString& aJobName, 147 nsCString& aAdjustedJobName) { 148 CopyUTF16toUTF8(aJobName, aAdjustedJobName); 149 150 if (aAdjustedJobName.Length() > IPP_JOB_NAME_LIMIT_LENGTH) { 151 uint32_t length = RewindToPriorUTF8Codepoint( 152 aAdjustedJobName.get(), (IPP_JOB_NAME_LIMIT_LENGTH - 3U)); 153 aAdjustedJobName.SetLength(length); 154 aAdjustedJobName.AppendLiteral("..."); 155 } 156 } 157 158 /* static */ 159 void PrintTarget::AdjustPrintJobNameForIPP(const nsAString& aJobName, 160 nsString& aAdjustedJobName) { 161 nsAutoCString jobName; 162 AdjustPrintJobNameForIPP(aJobName, jobName); 163 164 CopyUTF8toUTF16(jobName, aAdjustedJobName); 165 } 166 167 /* static */ 168 already_AddRefed<DrawTarget> PrintTarget::CreateRecordingDrawTarget( 169 DrawEventRecorder* aRecorder, DrawTarget* aDrawTarget) { 170 MOZ_ASSERT(aRecorder); 171 MOZ_ASSERT(aDrawTarget); 172 173 RefPtr<DrawTarget> dt; 174 175 if (aRecorder) { 176 // It doesn't really matter what we pass as the DrawTarget here. 177 dt = gfx::Factory::CreateRecordingDrawTarget(aRecorder, aDrawTarget, 178 aDrawTarget->GetRect()); 179 } 180 181 if (!dt || !dt->IsValid()) { 182 gfxCriticalNote 183 << "Failed to create a recording DrawTarget for PrintTarget"; 184 return nullptr; 185 } 186 187 return dt.forget(); 188 } 189 190 void PrintTarget::Finish() { 191 if (mIsFinished) { 192 return; 193 } 194 mIsFinished = true; 195 196 // null surfaces are allowed here 197 cairo_surface_finish(mCairoSurface); 198 } 199 200 } // namespace mozilla::gfx