How to use resetCtxToDefault method in wpt

Best JavaScript code snippet using wpt

canvas.js

Source:canvas.js Github

copy

Full Screen

...804 destCtx.setLineDash(sourceCtx.getLineDash());805 destCtx.lineDashOffset = sourceCtx.lineDashOffset;806 }807}808function resetCtxToDefault(ctx) {809 ctx.strokeStyle = "#000000";810 ctx.fillStyle = "#000000";811 ctx.fillRule = "nonzero";812 ctx.globalAlpha = 1;813 ctx.lineWidth = 1;814 ctx.lineCap = "butt";815 ctx.lineJoin = "miter";816 ctx.miterLimit = 10;817 ctx.globalCompositeOperation = "source-over";818 ctx.font = "10px sans-serif";819 if (ctx.setLineDash !== undefined) {820 ctx.setLineDash([]);821 ctx.lineDashOffset = 0;822 }823}824function composeSMaskBackdrop(bytes, r0, g0, b0) {825 const length = bytes.length;826 for (let i = 3; i < length; i += 4) {827 const alpha = bytes[i];828 if (alpha === 0) {829 bytes[i - 3] = r0;830 bytes[i - 2] = g0;831 bytes[i - 1] = b0;832 } else if (alpha < 255) {833 const alpha_ = 255 - alpha;834 bytes[i - 3] = (bytes[i - 3] * alpha + r0 * alpha_) >> 8;835 bytes[i - 2] = (bytes[i - 2] * alpha + g0 * alpha_) >> 8;836 bytes[i - 1] = (bytes[i - 1] * alpha + b0 * alpha_) >> 8;837 }838 }839}840function composeSMaskAlpha(maskData, layerData, transferMap) {841 const length = maskData.length;842 const scale = 1 / 255;843 for (let i = 3; i < length; i += 4) {844 const alpha = transferMap ? transferMap[maskData[i]] : maskData[i];845 layerData[i] = (layerData[i] * alpha * scale) | 0;846 }847}848function composeSMaskLuminosity(maskData, layerData, transferMap) {849 const length = maskData.length;850 for (let i = 3; i < length; i += 4) {851 const y =852 maskData[i - 3] * 77 + // * 0.3 / 255 * 0x10000853 maskData[i - 2] * 152 + // * 0.59 ....854 maskData[i - 1] * 28; // * 0.11 ....855 layerData[i] = transferMap856 ? (layerData[i] * transferMap[y >> 8]) >> 8857 : (layerData[i] * y) >> 16;858 }859}860function genericComposeSMask(861 maskCtx,862 layerCtx,863 width,864 height,865 subtype,866 backdrop,867 transferMap,868 layerOffsetX,869 layerOffsetY,870 maskOffsetX,871 maskOffsetY872) {873 const hasBackdrop = !!backdrop;874 const r0 = hasBackdrop ? backdrop[0] : 0;875 const g0 = hasBackdrop ? backdrop[1] : 0;876 const b0 = hasBackdrop ? backdrop[2] : 0;877 let composeFn;878 if (subtype === "Luminosity") {879 composeFn = composeSMaskLuminosity;880 } else {881 composeFn = composeSMaskAlpha;882 }883 // processing image in chunks to save memory884 const PIXELS_TO_PROCESS = 1048576;885 const chunkSize = Math.min(height, Math.ceil(PIXELS_TO_PROCESS / width));886 for (let row = 0; row < height; row += chunkSize) {887 const chunkHeight = Math.min(chunkSize, height - row);888 const maskData = maskCtx.getImageData(889 layerOffsetX - maskOffsetX,890 row + (layerOffsetY - maskOffsetY),891 width,892 chunkHeight893 );894 const layerData = layerCtx.getImageData(895 layerOffsetX,896 row + layerOffsetY,897 width,898 chunkHeight899 );900 if (hasBackdrop) {901 composeSMaskBackdrop(maskData.data, r0, g0, b0);902 }903 composeFn(maskData.data, layerData.data, transferMap);904 layerCtx.putImageData(layerData, layerOffsetX, row + layerOffsetY);905 }906}907function composeSMask(ctx, smask, layerCtx, layerBox) {908 const layerOffsetX = layerBox[0];909 const layerOffsetY = layerBox[1];910 const layerWidth = layerBox[2] - layerOffsetX;911 const layerHeight = layerBox[3] - layerOffsetY;912 if (layerWidth === 0 || layerHeight === 0) {913 return;914 }915 genericComposeSMask(916 smask.context,917 layerCtx,918 layerWidth,919 layerHeight,920 smask.subtype,921 smask.backdrop,922 smask.transferMap,923 layerOffsetX,924 layerOffsetY,925 smask.offsetX,926 smask.offsetY927 );928 ctx.save();929 ctx.globalAlpha = 1;930 ctx.globalCompositeOperation = "source-over";931 ctx.setTransform(1, 0, 0, 1, 0, 0);932 ctx.drawImage(layerCtx.canvas, 0, 0);933 ctx.restore();934}935function getImageSmoothingEnabled(transform, interpolate) {936 const scale = Util.singularValueDecompose2dScale(transform);937 // Round to a 32bit float so that `<=` check below will pass for numbers that938 // are very close, but not exactly the same 64bit floats.939 scale[0] = Math.fround(scale[0]);940 scale[1] = Math.fround(scale[1]);941 const actualScale = Math.fround(942 (globalThis.devicePixelRatio || 1) * PixelsPerInch.PDF_TO_CSS_UNITS943 );944 if (interpolate !== undefined) {945 // If the value is explicitly set use it.946 return interpolate;947 } else if (scale[0] <= actualScale || scale[1] <= actualScale) {948 // Smooth when downscaling.949 return true;950 }951 // Don't smooth when upscaling.952 return false;953}954const LINE_CAP_STYLES = ["butt", "round", "square"];955const LINE_JOIN_STYLES = ["miter", "round", "bevel"];956const NORMAL_CLIP = {};957const EO_CLIP = {};958class CanvasGraphics {959 constructor(960 canvasCtx,961 commonObjs,962 objs,963 canvasFactory,964 imageLayer,965 optionalContentConfig,966 annotationCanvasMap967 ) {968 this.ctx = canvasCtx;969 this.current = new CanvasExtraState(970 this.ctx.canvas.width,971 this.ctx.canvas.height972 );973 this.stateStack = [];974 this.pendingClip = null;975 this.pendingEOFill = false;976 this.res = null;977 this.xobjs = null;978 this.commonObjs = commonObjs;979 this.objs = objs;980 this.canvasFactory = canvasFactory;981 this.imageLayer = imageLayer;982 this.groupStack = [];983 this.processingType3 = null;984 // Patterns are painted relative to the initial page/form transform, see985 // PDF spec 8.7.2 NOTE 1.986 this.baseTransform = null;987 this.baseTransformStack = [];988 this.groupLevel = 0;989 this.smaskStack = [];990 this.smaskCounter = 0;991 this.tempSMask = null;992 this.suspendedCtx = null;993 this.contentVisible = true;994 this.markedContentStack = [];995 this.optionalContentConfig = optionalContentConfig;996 this.cachedCanvases = new CachedCanvases(this.canvasFactory);997 this.cachedPatterns = new Map();998 this.annotationCanvasMap = annotationCanvasMap;999 this.viewportScale = 1;1000 this.outputScaleX = 1;1001 this.outputScaleY = 1;1002 if (canvasCtx) {1003 // NOTE: if mozCurrentTransform is polyfilled, then the current state of1004 // the transformation must already be set in canvasCtx._transformMatrix.1005 addContextCurrentTransform(canvasCtx);1006 }1007 this._cachedScaleForStroking = null;1008 this._cachedGetSinglePixelWidth = null;1009 }1010 getObject(data, fallback = null) {1011 if (typeof data === "string") {1012 return data.startsWith("g_")1013 ? this.commonObjs.get(data)1014 : this.objs.get(data);1015 }1016 return fallback;1017 }1018 beginDrawing({1019 transform,1020 viewport,1021 transparency = false,1022 background = null,1023 }) {1024 // For pdfs that use blend modes we have to clear the canvas else certain1025 // blend modes can look wrong since we'd be blending with a white1026 // backdrop. The problem with a transparent backdrop though is we then1027 // don't get sub pixel anti aliasing on text, creating temporary1028 // transparent canvas when we have blend modes.1029 const width = this.ctx.canvas.width;1030 const height = this.ctx.canvas.height;1031 this.ctx.save();1032 this.ctx.fillStyle = background || "rgb(255, 255, 255)";1033 this.ctx.fillRect(0, 0, width, height);1034 this.ctx.restore();1035 if (transparency) {1036 const transparentCanvas = this.cachedCanvases.getCanvas(1037 "transparent",1038 width,1039 height,1040 true1041 );1042 this.compositeCtx = this.ctx;1043 this.transparentCanvas = transparentCanvas.canvas;1044 this.ctx = transparentCanvas.context;1045 this.ctx.save();1046 // The transform can be applied before rendering, transferring it to1047 // the new canvas.1048 this.ctx.transform.apply(this.ctx, this.compositeCtx.mozCurrentTransform);1049 }1050 this.ctx.save();1051 resetCtxToDefault(this.ctx);1052 if (transform) {1053 this.ctx.transform.apply(this.ctx, transform);1054 this.outputScaleX = transform[0];1055 this.outputScaleY = transform[0];1056 }1057 this.ctx.transform.apply(this.ctx, viewport.transform);1058 this.viewportScale = viewport.scale;1059 this.baseTransform = this.ctx.mozCurrentTransform.slice();1060 if (this.imageLayer) {1061 this.imageLayer.beginLayout();1062 }1063 }1064 executeOperatorList(1065 operatorList,1066 executionStartIdx,1067 continueCallback,1068 stepper1069 ) {1070 const argsArray = operatorList.argsArray;1071 const fnArray = operatorList.fnArray;1072 let i = executionStartIdx || 0;1073 const argsArrayLen = argsArray.length;1074 // Sometimes the OperatorList to execute is empty.1075 if (argsArrayLen === i) {1076 return i;1077 }1078 const chunkOperations =1079 argsArrayLen - i > EXECUTION_STEPS &&1080 typeof continueCallback === "function";1081 const endTime = chunkOperations ? Date.now() + EXECUTION_TIME : 0;1082 let steps = 0;1083 const commonObjs = this.commonObjs;1084 const objs = this.objs;1085 let fnId;1086 while (true) {1087 if (stepper !== undefined && i === stepper.nextBreakPoint) {1088 stepper.breakIt(i, continueCallback);1089 return i;1090 }1091 fnId = fnArray[i];1092 if (fnId !== OPS.dependency) {1093 this[fnId].apply(this, argsArray[i]);1094 } else {1095 for (const depObjId of argsArray[i]) {1096 const objsPool = depObjId.startsWith("g_") ? commonObjs : objs;1097 // If the promise isn't resolved yet, add the continueCallback1098 // to the promise and bail out.1099 if (!objsPool.has(depObjId)) {1100 objsPool.get(depObjId, continueCallback);1101 return i;1102 }1103 }1104 }1105 i++;1106 // If the entire operatorList was executed, stop as were done.1107 if (i === argsArrayLen) {1108 return i;1109 }1110 // If the execution took longer then a certain amount of time and1111 // `continueCallback` is specified, interrupt the execution.1112 if (chunkOperations && ++steps > EXECUTION_STEPS) {1113 if (Date.now() > endTime) {1114 continueCallback();1115 return i;1116 }1117 steps = 0;1118 }1119 // If the operatorList isn't executed completely yet OR the execution1120 // time was short enough, do another execution round.1121 }1122 }1123 endDrawing() {1124 // Finishing all opened operations such as SMask group painting.1125 while (this.stateStack.length || this.inSMaskMode) {1126 this.restore();1127 }1128 this.ctx.restore();1129 if (this.transparentCanvas) {1130 this.ctx = this.compositeCtx;1131 this.ctx.save();1132 this.ctx.setTransform(1, 0, 0, 1, 0, 0); // Avoid apply transform twice1133 this.ctx.drawImage(this.transparentCanvas, 0, 0);1134 this.ctx.restore();1135 this.transparentCanvas = null;1136 }1137 this.cachedCanvases.clear();1138 this.cachedPatterns.clear();1139 if (this.imageLayer) {1140 this.imageLayer.endLayout();1141 }1142 }1143 _scaleImage(img, inverseTransform) {1144 // Vertical or horizontal scaling shall not be more than 2 to not lose the1145 // pixels during drawImage operation, painting on the temporary canvas(es)1146 // that are twice smaller in size.1147 const width = img.width;1148 const height = img.height;1149 let widthScale = Math.max(1150 Math.hypot(inverseTransform[0], inverseTransform[1]),1151 11152 );1153 let heightScale = Math.max(1154 Math.hypot(inverseTransform[2], inverseTransform[3]),1155 11156 );1157 let paintWidth = width,1158 paintHeight = height;1159 let tmpCanvasId = "prescale1";1160 let tmpCanvas, tmpCtx;1161 while (1162 (widthScale > 2 && paintWidth > 1) ||1163 (heightScale > 2 && paintHeight > 1)1164 ) {1165 let newWidth = paintWidth,1166 newHeight = paintHeight;1167 if (widthScale > 2 && paintWidth > 1) {1168 newWidth = Math.ceil(paintWidth / 2);1169 widthScale /= paintWidth / newWidth;1170 }1171 if (heightScale > 2 && paintHeight > 1) {1172 newHeight = Math.ceil(paintHeight / 2);1173 heightScale /= paintHeight / newHeight;1174 }1175 tmpCanvas = this.cachedCanvases.getCanvas(1176 tmpCanvasId,1177 newWidth,1178 newHeight1179 );1180 tmpCtx = tmpCanvas.context;1181 tmpCtx.clearRect(0, 0, newWidth, newHeight);1182 tmpCtx.drawImage(1183 img,1184 0,1185 0,1186 paintWidth,1187 paintHeight,1188 0,1189 0,1190 newWidth,1191 newHeight1192 );1193 img = tmpCanvas.canvas;1194 paintWidth = newWidth;1195 paintHeight = newHeight;1196 tmpCanvasId = tmpCanvasId === "prescale1" ? "prescale2" : "prescale1";1197 }1198 return {1199 img,1200 paintWidth,1201 paintHeight,1202 };1203 }1204 _createMaskCanvas(img) {1205 const ctx = this.ctx;1206 const width = img.width,1207 height = img.height;1208 const fillColor = this.current.fillColor;1209 const isPatternFill = this.current.patternFill;1210 const maskCanvas = this.cachedCanvases.getCanvas(1211 "maskCanvas",1212 width,1213 height1214 );1215 const maskCtx = maskCanvas.context;1216 putBinaryImageMask(maskCtx, img);1217 // Create the mask canvas at the size it will be drawn at and also set1218 // its transform to match the current transform so if there are any1219 // patterns applied they will be applied relative to the correct1220 // transform.1221 const objToCanvas = ctx.mozCurrentTransform;1222 let maskToCanvas = Util.transform(objToCanvas, [1223 1 / width,1224 0,1225 0,1226 -1 / height,1227 0,1228 0,1229 ]);1230 maskToCanvas = Util.transform(maskToCanvas, [1, 0, 0, 1, 0, -height]);1231 const cord1 = Util.applyTransform([0, 0], maskToCanvas);1232 const cord2 = Util.applyTransform([width, height], maskToCanvas);1233 const rect = Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]);1234 const drawnWidth = Math.ceil(rect[2] - rect[0]);1235 const drawnHeight = Math.ceil(rect[3] - rect[1]);1236 const fillCanvas = this.cachedCanvases.getCanvas(1237 "fillCanvas",1238 drawnWidth,1239 drawnHeight,1240 true1241 );1242 const fillCtx = fillCanvas.context;1243 // The offset will be the top-left cordinate mask.1244 const offsetX = Math.min(cord1[0], cord2[0]);1245 const offsetY = Math.min(cord1[1], cord2[1]);1246 fillCtx.translate(-offsetX, -offsetY);1247 fillCtx.transform.apply(fillCtx, maskToCanvas);1248 // Pre-scale if needed to improve image smoothing.1249 const scaled = this._scaleImage(1250 maskCanvas.canvas,1251 fillCtx.mozCurrentTransformInverse1252 );1253 fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(1254 fillCtx.mozCurrentTransform,1255 img.interpolate1256 );1257 fillCtx.drawImage(1258 scaled.img,1259 0,1260 0,1261 scaled.img.width,1262 scaled.img.height,1263 0,1264 0,1265 width,1266 height1267 );1268 fillCtx.globalCompositeOperation = "source-in";1269 const inverse = Util.transform(fillCtx.mozCurrentTransformInverse, [1270 1,1271 0,1272 0,1273 1,1274 -offsetX,1275 -offsetY,1276 ]);1277 fillCtx.fillStyle = isPatternFill1278 ? fillColor.getPattern(ctx, this, inverse, PathType.FILL)1279 : fillColor;1280 fillCtx.fillRect(0, 0, width, height);1281 // Round the offsets to avoid drawing fractional pixels.1282 return {1283 canvas: fillCanvas.canvas,1284 offsetX: Math.round(offsetX),1285 offsetY: Math.round(offsetY),1286 };1287 }1288 // Graphics state1289 setLineWidth(width) {1290 if (width !== this.current.lineWidth) {1291 this._cachedScaleForStroking = null;1292 }1293 this.current.lineWidth = width;1294 this.ctx.lineWidth = width;1295 }1296 setLineCap(style) {1297 this.ctx.lineCap = LINE_CAP_STYLES[style];1298 }1299 setLineJoin(style) {1300 this.ctx.lineJoin = LINE_JOIN_STYLES[style];1301 }1302 setMiterLimit(limit) {1303 this.ctx.miterLimit = limit;1304 }1305 setDash(dashArray, dashPhase) {1306 const ctx = this.ctx;1307 if (ctx.setLineDash !== undefined) {1308 ctx.setLineDash(dashArray);1309 ctx.lineDashOffset = dashPhase;1310 }1311 }1312 setRenderingIntent(intent) {1313 // This operation is ignored since we haven't found a use case for it yet.1314 }1315 setFlatness(flatness) {1316 // This operation is ignored since we haven't found a use case for it yet.1317 }1318 setGState(states) {1319 for (let i = 0, ii = states.length; i < ii; i++) {1320 const state = states[i];1321 const key = state[0];1322 const value = state[1];1323 switch (key) {1324 case "LW":1325 this.setLineWidth(value);1326 break;1327 case "LC":1328 this.setLineCap(value);1329 break;1330 case "LJ":1331 this.setLineJoin(value);1332 break;1333 case "ML":1334 this.setMiterLimit(value);1335 break;1336 case "D":1337 this.setDash(value[0], value[1]);1338 break;1339 case "RI":1340 this.setRenderingIntent(value);1341 break;1342 case "FL":1343 this.setFlatness(value);1344 break;1345 case "Font":1346 this.setFont(value[0], value[1]);1347 break;1348 case "CA":1349 this.current.strokeAlpha = state[1];1350 break;1351 case "ca":1352 this.current.fillAlpha = state[1];1353 this.ctx.globalAlpha = state[1];1354 break;1355 case "BM":1356 this.ctx.globalCompositeOperation = value;1357 break;1358 case "SMask":1359 this.current.activeSMask = value ? this.tempSMask : null;1360 this.tempSMask = null;1361 this.checkSMaskState();1362 break;1363 case "TR":1364 this.current.transferMaps = value;1365 }1366 }1367 }1368 get inSMaskMode() {1369 return !!this.suspendedCtx;1370 }1371 checkSMaskState() {1372 const inSMaskMode = this.inSMaskMode;1373 if (this.current.activeSMask && !inSMaskMode) {1374 this.beginSMaskMode();1375 } else if (!this.current.activeSMask && inSMaskMode) {1376 this.endSMaskMode();1377 }1378 // Else, the state is okay and nothing needs to be done.1379 }1380 /**1381 * Soft mask mode takes the current main drawing canvas and replaces it with1382 * a temporary canvas. Any drawing operations that happen on the temporary1383 * canvas need to be composed with the main canvas that was suspended (see1384 * `compose()`). The temporary canvas also duplicates many of its operations1385 * on the suspended canvas to keep them in sync, so that when the soft mask1386 * mode ends any clipping paths or transformations will still be active and in1387 * the right order on the canvas' graphics state stack.1388 */1389 beginSMaskMode() {1390 if (this.inSMaskMode) {1391 throw new Error("beginSMaskMode called while already in smask mode");1392 }1393 const drawnWidth = this.ctx.canvas.width;1394 const drawnHeight = this.ctx.canvas.height;1395 const cacheId = "smaskGroupAt" + this.groupLevel;1396 const scratchCanvas = this.cachedCanvases.getCanvas(1397 cacheId,1398 drawnWidth,1399 drawnHeight,1400 true1401 );1402 this.suspendedCtx = this.ctx;1403 this.ctx = scratchCanvas.context;1404 const ctx = this.ctx;1405 ctx.setTransform.apply(ctx, this.suspendedCtx.mozCurrentTransform);1406 copyCtxState(this.suspendedCtx, ctx);1407 mirrorContextOperations(ctx, this.suspendedCtx);1408 this.setGState([1409 ["BM", "source-over"],1410 ["ca", 1],1411 ["CA", 1],1412 ]);1413 }1414 endSMaskMode() {1415 if (!this.inSMaskMode) {1416 throw new Error("endSMaskMode called while not in smask mode");1417 }1418 // The soft mask is done, now restore the suspended canvas as the main1419 // drawing canvas.1420 this.ctx._removeMirroring();1421 copyCtxState(this.ctx, this.suspendedCtx);1422 this.ctx = this.suspendedCtx;1423 this.suspendedCtx = null;1424 }1425 compose(dirtyBox) {1426 if (!this.current.activeSMask) {1427 return;1428 }1429 if (!dirtyBox) {1430 dirtyBox = [0, 0, this.ctx.canvas.width, this.ctx.canvas.height];1431 } else {1432 dirtyBox[0] = Math.floor(dirtyBox[0]);1433 dirtyBox[1] = Math.floor(dirtyBox[1]);1434 dirtyBox[2] = Math.ceil(dirtyBox[2]);1435 dirtyBox[3] = Math.ceil(dirtyBox[3]);1436 }1437 const smask = this.current.activeSMask;1438 const suspendedCtx = this.suspendedCtx;1439 composeSMask(suspendedCtx, smask, this.ctx, dirtyBox);1440 // Whatever was drawn has been moved to the suspended canvas, now clear it1441 // out of the current canvas.1442 this.ctx.save();1443 this.ctx.setTransform(1, 0, 0, 1, 0, 0);1444 this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);1445 this.ctx.restore();1446 }1447 save() {1448 if (this.inSMaskMode) {1449 // SMask mode may be turned on/off causing us to lose graphics state.1450 // Copy the temporary canvas state to the main(suspended) canvas to keep1451 // it in sync.1452 copyCtxState(this.ctx, this.suspendedCtx);1453 // Don't bother calling save on the temporary canvas since state is not1454 // saved there.1455 this.suspendedCtx.save();1456 } else {1457 this.ctx.save();1458 }1459 const old = this.current;1460 this.stateStack.push(old);1461 this.current = old.clone();1462 }1463 restore() {1464 if (this.stateStack.length === 0 && this.inSMaskMode) {1465 this.endSMaskMode();1466 }1467 if (this.stateStack.length !== 0) {1468 this.current = this.stateStack.pop();1469 if (this.inSMaskMode) {1470 // Graphics state is stored on the main(suspended) canvas. Restore its1471 // state then copy it over to the temporary canvas.1472 this.suspendedCtx.restore();1473 copyCtxState(this.suspendedCtx, this.ctx);1474 } else {1475 this.ctx.restore();1476 }1477 this.checkSMaskState();1478 // Ensure that the clipping path is reset (fixes issue6413.pdf).1479 this.pendingClip = null;1480 this._cachedScaleForStroking = null;1481 this._cachedGetSinglePixelWidth = null;1482 }1483 }1484 transform(a, b, c, d, e, f) {1485 this.ctx.transform(a, b, c, d, e, f);1486 this._cachedScaleForStroking = null;1487 this._cachedGetSinglePixelWidth = null;1488 }1489 // Path1490 constructPath(ops, args) {1491 const ctx = this.ctx;1492 const current = this.current;1493 let x = current.x,1494 y = current.y;1495 let startX, startY;1496 for (let i = 0, j = 0, ii = ops.length; i < ii; i++) {1497 switch (ops[i] | 0) {1498 case OPS.rectangle:1499 x = args[j++];1500 y = args[j++];1501 const width = args[j++];1502 const height = args[j++];1503 const xw = x + width;1504 const yh = y + height;1505 ctx.moveTo(x, y);1506 if (width === 0 || height === 0) {1507 ctx.lineTo(xw, yh);1508 } else {1509 ctx.lineTo(xw, y);1510 ctx.lineTo(xw, yh);1511 ctx.lineTo(x, yh);1512 }1513 current.updatePathMinMax(ctx.mozCurrentTransform, x, y);1514 current.updatePathMinMax(ctx.mozCurrentTransform, xw, yh);1515 ctx.closePath();1516 break;1517 case OPS.moveTo:1518 x = args[j++];1519 y = args[j++];1520 ctx.moveTo(x, y);1521 current.updatePathMinMax(ctx.mozCurrentTransform, x, y);1522 break;1523 case OPS.lineTo:1524 x = args[j++];1525 y = args[j++];1526 ctx.lineTo(x, y);1527 current.updatePathMinMax(ctx.mozCurrentTransform, x, y);1528 break;1529 case OPS.curveTo:1530 startX = x;1531 startY = y;1532 x = args[j + 4];1533 y = args[j + 5];1534 ctx.bezierCurveTo(1535 args[j],1536 args[j + 1],1537 args[j + 2],1538 args[j + 3],1539 x,1540 y1541 );1542 current.updateCurvePathMinMax(1543 ctx.mozCurrentTransform,1544 startX,1545 startY,1546 args[j],1547 args[j + 1],1548 args[j + 2],1549 args[j + 3],1550 x,1551 y1552 );1553 j += 6;1554 break;1555 case OPS.curveTo2:1556 startX = x;1557 startY = y;1558 ctx.bezierCurveTo(1559 x,1560 y,1561 args[j],1562 args[j + 1],1563 args[j + 2],1564 args[j + 3]1565 );1566 current.updateCurvePathMinMax(1567 ctx.mozCurrentTransform,1568 startX,1569 startY,1570 x,1571 y,1572 args[j],1573 args[j + 1],1574 args[j + 2],1575 args[j + 3]1576 );1577 x = args[j + 2];1578 y = args[j + 3];1579 j += 4;1580 break;1581 case OPS.curveTo3:1582 startX = x;1583 startY = y;1584 x = args[j + 2];1585 y = args[j + 3];1586 ctx.bezierCurveTo(args[j], args[j + 1], x, y, x, y);1587 current.updateCurvePathMinMax(1588 ctx.mozCurrentTransform,1589 startX,1590 startY,1591 args[j],1592 args[j + 1],1593 x,1594 y,1595 x,1596 y1597 );1598 j += 4;1599 break;1600 case OPS.closePath:1601 ctx.closePath();1602 break;1603 }1604 }1605 current.setCurrentPoint(x, y);1606 }1607 closePath() {1608 this.ctx.closePath();1609 }1610 stroke(consumePath) {1611 consumePath = typeof consumePath !== "undefined" ? consumePath : true;1612 const ctx = this.ctx;1613 const strokeColor = this.current.strokeColor;1614 // For stroke we want to temporarily change the global alpha to the1615 // stroking alpha.1616 ctx.globalAlpha = this.current.strokeAlpha;1617 if (this.contentVisible) {1618 if (typeof strokeColor === "object" && strokeColor?.getPattern) {1619 ctx.save();1620 ctx.strokeStyle = strokeColor.getPattern(1621 ctx,1622 this,1623 ctx.mozCurrentTransformInverse,1624 PathType.STROKE1625 );1626 this.rescaleAndStroke(/* saveRestore */ false);1627 ctx.restore();1628 } else {1629 this.rescaleAndStroke(/* saveRestore */ true);1630 }1631 }1632 if (consumePath) {1633 this.consumePath(this.current.getClippedPathBoundingBox());1634 }1635 // Restore the global alpha to the fill alpha1636 ctx.globalAlpha = this.current.fillAlpha;1637 }1638 closeStroke() {1639 this.closePath();1640 this.stroke();1641 }1642 fill(consumePath) {1643 consumePath = typeof consumePath !== "undefined" ? consumePath : true;1644 const ctx = this.ctx;1645 const fillColor = this.current.fillColor;1646 const isPatternFill = this.current.patternFill;1647 let needRestore = false;1648 if (isPatternFill) {1649 ctx.save();1650 ctx.fillStyle = fillColor.getPattern(1651 ctx,1652 this,1653 ctx.mozCurrentTransformInverse,1654 PathType.FILL1655 );1656 needRestore = true;1657 }1658 const intersect = this.current.getClippedPathBoundingBox();1659 if (this.contentVisible && intersect !== null) {1660 if (this.pendingEOFill) {1661 ctx.fill("evenodd");1662 this.pendingEOFill = false;1663 } else {1664 ctx.fill();1665 }1666 }1667 if (needRestore) {1668 ctx.restore();1669 }1670 if (consumePath) {1671 this.consumePath(intersect);1672 }1673 }1674 eoFill() {1675 this.pendingEOFill = true;1676 this.fill();1677 }1678 fillStroke() {1679 this.fill(false);1680 this.stroke(false);1681 this.consumePath();1682 }1683 eoFillStroke() {1684 this.pendingEOFill = true;1685 this.fillStroke();1686 }1687 closeFillStroke() {1688 this.closePath();1689 this.fillStroke();1690 }1691 closeEOFillStroke() {1692 this.pendingEOFill = true;1693 this.closePath();1694 this.fillStroke();1695 }1696 endPath() {1697 this.consumePath();1698 }1699 // Clipping1700 clip() {1701 this.pendingClip = NORMAL_CLIP;1702 }1703 eoClip() {1704 this.pendingClip = EO_CLIP;1705 }1706 // Text1707 beginText() {1708 this.current.textMatrix = IDENTITY_MATRIX;1709 this.current.textMatrixScale = 1;1710 this.current.x = this.current.lineX = 0;1711 this.current.y = this.current.lineY = 0;1712 }1713 endText() {1714 const paths = this.pendingTextPaths;1715 const ctx = this.ctx;1716 if (paths === undefined) {1717 ctx.beginPath();1718 return;1719 }1720 ctx.save();1721 ctx.beginPath();1722 for (let i = 0; i < paths.length; i++) {1723 const path = paths[i];1724 ctx.setTransform.apply(ctx, path.transform);1725 ctx.translate(path.x, path.y);1726 path.addToPath(ctx, path.fontSize);1727 }1728 ctx.restore();1729 ctx.clip();1730 ctx.beginPath();1731 delete this.pendingTextPaths;1732 }1733 setCharSpacing(spacing) {1734 this.current.charSpacing = spacing;1735 }1736 setWordSpacing(spacing) {1737 this.current.wordSpacing = spacing;1738 }1739 setHScale(scale) {1740 this.current.textHScale = scale / 100;1741 }1742 setLeading(leading) {1743 this.current.leading = -leading;1744 }1745 setFont(fontRefName, size) {1746 const fontObj = this.commonObjs.get(fontRefName);1747 const current = this.current;1748 if (!fontObj) {1749 throw new Error(`Can't find font for ${fontRefName}`);1750 }1751 current.fontMatrix = fontObj.fontMatrix || FONT_IDENTITY_MATRIX;1752 // A valid matrix needs all main diagonal elements to be non-zero1753 // This also ensures we bypass FF bugzilla bug #719844.1754 if (current.fontMatrix[0] === 0 || current.fontMatrix[3] === 0) {1755 warn("Invalid font matrix for font " + fontRefName);1756 }1757 // The spec for Tf (setFont) says that 'size' specifies the font 'scale',1758 // and in some docs this can be negative (inverted x-y axes).1759 if (size < 0) {1760 size = -size;1761 current.fontDirection = -1;1762 } else {1763 current.fontDirection = 1;1764 }1765 this.current.font = fontObj;1766 this.current.fontSize = size;1767 if (fontObj.isType3Font) {1768 return; // we don't need ctx.font for Type3 fonts1769 }1770 const name = fontObj.loadedName || "sans-serif";1771 let bold = "normal";1772 if (fontObj.black) {1773 bold = "900";1774 } else if (fontObj.bold) {1775 bold = "bold";1776 }1777 const italic = fontObj.italic ? "italic" : "normal";1778 const typeface = `"${name}", ${fontObj.fallbackName}`;1779 // Some font backends cannot handle fonts below certain size.1780 // Keeping the font at minimal size and using the fontSizeScale to change1781 // the current transformation matrix before the fillText/strokeText.1782 // See https://bugzilla.mozilla.org/show_bug.cgi?id=7262271783 let browserFontSize = size;1784 if (size < MIN_FONT_SIZE) {1785 browserFontSize = MIN_FONT_SIZE;1786 } else if (size > MAX_FONT_SIZE) {1787 browserFontSize = MAX_FONT_SIZE;1788 }1789 this.current.fontSizeScale = size / browserFontSize;1790 this.ctx.font = `${italic} ${bold} ${browserFontSize}px ${typeface}`;1791 }1792 setTextRenderingMode(mode) {1793 this.current.textRenderingMode = mode;1794 }1795 setTextRise(rise) {1796 this.current.textRise = rise;1797 }1798 moveText(x, y) {1799 this.current.x = this.current.lineX += x;1800 this.current.y = this.current.lineY += y;1801 }1802 setLeadingMoveText(x, y) {1803 this.setLeading(-y);1804 this.moveText(x, y);1805 }1806 setTextMatrix(a, b, c, d, e, f) {1807 this.current.textMatrix = [a, b, c, d, e, f];1808 this.current.textMatrixScale = Math.hypot(a, b);1809 this.current.x = this.current.lineX = 0;1810 this.current.y = this.current.lineY = 0;1811 }1812 nextLine() {1813 this.moveText(0, this.current.leading);1814 }1815 paintChar(character, x, y, patternTransform) {1816 const ctx = this.ctx;1817 const current = this.current;1818 const font = current.font;1819 const textRenderingMode = current.textRenderingMode;1820 const fontSize = current.fontSize / current.fontSizeScale;1821 const fillStrokeMode =1822 textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;1823 const isAddToPathSet = !!(1824 textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG1825 );1826 const patternFill = current.patternFill && !font.missingFile;1827 let addToPath;1828 if (font.disableFontFace || isAddToPathSet || patternFill) {1829 addToPath = font.getPathGenerator(this.commonObjs, character);1830 }1831 if (font.disableFontFace || patternFill) {1832 ctx.save();1833 ctx.translate(x, y);1834 ctx.beginPath();1835 addToPath(ctx, fontSize);1836 if (patternTransform) {1837 ctx.setTransform.apply(ctx, patternTransform);1838 }1839 if (1840 fillStrokeMode === TextRenderingMode.FILL ||1841 fillStrokeMode === TextRenderingMode.FILL_STROKE1842 ) {1843 ctx.fill();1844 }1845 if (1846 fillStrokeMode === TextRenderingMode.STROKE ||1847 fillStrokeMode === TextRenderingMode.FILL_STROKE1848 ) {1849 ctx.stroke();1850 }1851 ctx.restore();1852 } else {1853 if (1854 fillStrokeMode === TextRenderingMode.FILL ||1855 fillStrokeMode === TextRenderingMode.FILL_STROKE1856 ) {1857 ctx.fillText(character, x, y);1858 }1859 if (1860 fillStrokeMode === TextRenderingMode.STROKE ||1861 fillStrokeMode === TextRenderingMode.FILL_STROKE1862 ) {1863 ctx.strokeText(character, x, y);1864 }1865 }1866 if (isAddToPathSet) {1867 const paths = this.pendingTextPaths || (this.pendingTextPaths = []);1868 paths.push({1869 transform: ctx.mozCurrentTransform,1870 x,1871 y,1872 fontSize,1873 addToPath,1874 });1875 }1876 }1877 get isFontSubpixelAAEnabled() {1878 // Checks if anti-aliasing is enabled when scaled text is painted.1879 // On Windows GDI scaled fonts looks bad.1880 const { context: ctx } = this.cachedCanvases.getCanvas(1881 "isFontSubpixelAAEnabled",1882 10,1883 101884 );1885 ctx.scale(1.5, 1);1886 ctx.fillText("I", 0, 10);1887 const data = ctx.getImageData(0, 0, 10, 10).data;1888 let enabled = false;1889 for (let i = 3; i < data.length; i += 4) {1890 if (data[i] > 0 && data[i] < 255) {1891 enabled = true;1892 break;1893 }1894 }1895 return shadow(this, "isFontSubpixelAAEnabled", enabled);1896 }1897 showText(glyphs) {1898 const current = this.current;1899 const font = current.font;1900 if (font.isType3Font) {1901 return this.showType3Text(glyphs);1902 }1903 const fontSize = current.fontSize;1904 if (fontSize === 0) {1905 return undefined;1906 }1907 const ctx = this.ctx;1908 const fontSizeScale = current.fontSizeScale;1909 const charSpacing = current.charSpacing;1910 const wordSpacing = current.wordSpacing;1911 const fontDirection = current.fontDirection;1912 const textHScale = current.textHScale * fontDirection;1913 const glyphsLength = glyphs.length;1914 const vertical = font.vertical;1915 const spacingDir = vertical ? 1 : -1;1916 const defaultVMetrics = font.defaultVMetrics;1917 const widthAdvanceScale = fontSize * current.fontMatrix[0];1918 const simpleFillText =1919 current.textRenderingMode === TextRenderingMode.FILL &&1920 !font.disableFontFace &&1921 !current.patternFill;1922 ctx.save();1923 ctx.transform.apply(ctx, current.textMatrix);1924 ctx.translate(current.x, current.y + current.textRise);1925 if (fontDirection > 0) {1926 ctx.scale(textHScale, -1);1927 } else {1928 ctx.scale(textHScale, 1);1929 }1930 let patternTransform;1931 if (current.patternFill) {1932 ctx.save();1933 const pattern = current.fillColor.getPattern(1934 ctx,1935 this,1936 ctx.mozCurrentTransformInverse,1937 PathType.FILL1938 );1939 patternTransform = ctx.mozCurrentTransform;1940 ctx.restore();1941 ctx.fillStyle = pattern;1942 }1943 let lineWidth = current.lineWidth;1944 const scale = current.textMatrixScale;1945 if (scale === 0 || lineWidth === 0) {1946 const fillStrokeMode =1947 current.textRenderingMode & TextRenderingMode.FILL_STROKE_MASK;1948 if (1949 fillStrokeMode === TextRenderingMode.STROKE ||1950 fillStrokeMode === TextRenderingMode.FILL_STROKE1951 ) {1952 lineWidth = this.getSinglePixelWidth();1953 }1954 } else {1955 lineWidth /= scale;1956 }1957 if (fontSizeScale !== 1.0) {1958 ctx.scale(fontSizeScale, fontSizeScale);1959 lineWidth /= fontSizeScale;1960 }1961 ctx.lineWidth = lineWidth;1962 let x = 0,1963 i;1964 for (i = 0; i < glyphsLength; ++i) {1965 const glyph = glyphs[i];1966 if (typeof glyph === "number") {1967 x += (spacingDir * glyph * fontSize) / 1000;1968 continue;1969 }1970 let restoreNeeded = false;1971 const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;1972 const character = glyph.fontChar;1973 const accent = glyph.accent;1974 let scaledX, scaledY;1975 let width = glyph.width;1976 if (vertical) {1977 const vmetric = glyph.vmetric || defaultVMetrics;1978 const vx =1979 -(glyph.vmetric ? vmetric[1] : width * 0.5) * widthAdvanceScale;1980 const vy = vmetric[2] * widthAdvanceScale;1981 width = vmetric ? -vmetric[0] : width;1982 scaledX = vx / fontSizeScale;1983 scaledY = (x + vy) / fontSizeScale;1984 } else {1985 scaledX = x / fontSizeScale;1986 scaledY = 0;1987 }1988 if (font.remeasure && width > 0) {1989 // Some standard fonts may not have the exact width: rescale per1990 // character if measured width is greater than expected glyph width1991 // and subpixel-aa is enabled, otherwise just center the glyph.1992 const measuredWidth =1993 ((ctx.measureText(character).width * 1000) / fontSize) *1994 fontSizeScale;1995 if (width < measuredWidth && this.isFontSubpixelAAEnabled) {1996 const characterScaleX = width / measuredWidth;1997 restoreNeeded = true;1998 ctx.save();1999 ctx.scale(characterScaleX, 1);2000 scaledX /= characterScaleX;2001 } else if (width !== measuredWidth) {2002 scaledX +=2003 (((width - measuredWidth) / 2000) * fontSize) / fontSizeScale;2004 }2005 }2006 // Only attempt to draw the glyph if it is actually in the embedded font2007 // file or if there isn't a font file so the fallback font is shown.2008 if (this.contentVisible && (glyph.isInFont || font.missingFile)) {2009 if (simpleFillText && !accent) {2010 // common case2011 ctx.fillText(character, scaledX, scaledY);2012 } else {2013 this.paintChar(character, scaledX, scaledY, patternTransform);2014 if (accent) {2015 const scaledAccentX =2016 scaledX + (fontSize * accent.offset.x) / fontSizeScale;2017 const scaledAccentY =2018 scaledY - (fontSize * accent.offset.y) / fontSizeScale;2019 this.paintChar(2020 accent.fontChar,2021 scaledAccentX,2022 scaledAccentY,2023 patternTransform2024 );2025 }2026 }2027 }2028 let charWidth;2029 if (vertical) {2030 charWidth = width * widthAdvanceScale - spacing * fontDirection;2031 } else {2032 charWidth = width * widthAdvanceScale + spacing * fontDirection;2033 }2034 x += charWidth;2035 if (restoreNeeded) {2036 ctx.restore();2037 }2038 }2039 if (vertical) {2040 current.y -= x;2041 } else {2042 current.x += x * textHScale;2043 }2044 ctx.restore();2045 this.compose();2046 return undefined;2047 }2048 showType3Text(glyphs) {2049 // Type3 fonts - each glyph is a "mini-PDF"2050 const ctx = this.ctx;2051 const current = this.current;2052 const font = current.font;2053 const fontSize = current.fontSize;2054 const fontDirection = current.fontDirection;2055 const spacingDir = font.vertical ? 1 : -1;2056 const charSpacing = current.charSpacing;2057 const wordSpacing = current.wordSpacing;2058 const textHScale = current.textHScale * fontDirection;2059 const fontMatrix = current.fontMatrix || FONT_IDENTITY_MATRIX;2060 const glyphsLength = glyphs.length;2061 const isTextInvisible =2062 current.textRenderingMode === TextRenderingMode.INVISIBLE;2063 let i, glyph, width, spacingLength;2064 if (isTextInvisible || fontSize === 0) {2065 return;2066 }2067 this._cachedScaleForStroking = null;2068 this._cachedGetSinglePixelWidth = null;2069 ctx.save();2070 ctx.transform.apply(ctx, current.textMatrix);2071 ctx.translate(current.x, current.y);2072 ctx.scale(textHScale, fontDirection);2073 for (i = 0; i < glyphsLength; ++i) {2074 glyph = glyphs[i];2075 if (typeof glyph === "number") {2076 spacingLength = (spacingDir * glyph * fontSize) / 1000;2077 this.ctx.translate(spacingLength, 0);2078 current.x += spacingLength * textHScale;2079 continue;2080 }2081 const spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing;2082 const operatorList = font.charProcOperatorList[glyph.operatorListId];2083 if (!operatorList) {2084 warn(`Type3 character "${glyph.operatorListId}" is not available.`);2085 continue;2086 }2087 if (this.contentVisible) {2088 this.processingType3 = glyph;2089 this.save();2090 ctx.scale(fontSize, fontSize);2091 ctx.transform.apply(ctx, fontMatrix);2092 this.executeOperatorList(operatorList);2093 this.restore();2094 }2095 const transformed = Util.applyTransform([glyph.width, 0], fontMatrix);2096 width = transformed[0] * fontSize + spacing;2097 ctx.translate(width, 0);2098 current.x += width * textHScale;2099 }2100 ctx.restore();2101 this.processingType3 = null;2102 }2103 // Type3 fonts2104 setCharWidth(xWidth, yWidth) {2105 // We can safely ignore this since the width should be the same2106 // as the width in the Widths array.2107 }2108 setCharWidthAndBounds(xWidth, yWidth, llx, lly, urx, ury) {2109 // TODO According to the spec we're also suppose to ignore any operators2110 // that set color or include images while processing this type3 font.2111 this.ctx.rect(llx, lly, urx - llx, ury - lly);2112 this.clip();2113 this.endPath();2114 }2115 // Color2116 getColorN_Pattern(IR) {2117 let pattern;2118 if (IR[0] === "TilingPattern") {2119 const color = IR[1];2120 const baseTransform =2121 this.baseTransform || this.ctx.mozCurrentTransform.slice();2122 const canvasGraphicsFactory = {2123 createCanvasGraphics: ctx => {2124 return new CanvasGraphics(2125 ctx,2126 this.commonObjs,2127 this.objs,2128 this.canvasFactory2129 );2130 },2131 };2132 pattern = new TilingPattern(2133 IR,2134 color,2135 this.ctx,2136 canvasGraphicsFactory,2137 baseTransform2138 );2139 } else {2140 pattern = this._getPattern(IR[1], IR[2]);2141 }2142 return pattern;2143 }2144 setStrokeColorN() {2145 this.current.strokeColor = this.getColorN_Pattern(arguments);2146 }2147 setFillColorN() {2148 this.current.fillColor = this.getColorN_Pattern(arguments);2149 this.current.patternFill = true;2150 }2151 setStrokeRGBColor(r, g, b) {2152 const color = Util.makeHexColor(r, g, b);2153 this.ctx.strokeStyle = color;2154 this.current.strokeColor = color;2155 }2156 setFillRGBColor(r, g, b) {2157 const color = Util.makeHexColor(r, g, b);2158 this.ctx.fillStyle = color;2159 this.current.fillColor = color;2160 this.current.patternFill = false;2161 }2162 _getPattern(objId, matrix = null) {2163 let pattern;2164 if (this.cachedPatterns.has(objId)) {2165 pattern = this.cachedPatterns.get(objId);2166 } else {2167 pattern = getShadingPattern(this.objs.get(objId));2168 this.cachedPatterns.set(objId, pattern);2169 }2170 if (matrix) {2171 pattern.matrix = matrix;2172 }2173 return pattern;2174 }2175 shadingFill(objId) {2176 if (!this.contentVisible) {2177 return;2178 }2179 const ctx = this.ctx;2180 this.save();2181 const pattern = this._getPattern(objId);2182 ctx.fillStyle = pattern.getPattern(2183 ctx,2184 this,2185 ctx.mozCurrentTransformInverse,2186 PathType.SHADING2187 );2188 const inv = ctx.mozCurrentTransformInverse;2189 if (inv) {2190 const canvas = ctx.canvas;2191 const width = canvas.width;2192 const height = canvas.height;2193 const bl = Util.applyTransform([0, 0], inv);2194 const br = Util.applyTransform([0, height], inv);2195 const ul = Util.applyTransform([width, 0], inv);2196 const ur = Util.applyTransform([width, height], inv);2197 const x0 = Math.min(bl[0], br[0], ul[0], ur[0]);2198 const y0 = Math.min(bl[1], br[1], ul[1], ur[1]);2199 const x1 = Math.max(bl[0], br[0], ul[0], ur[0]);2200 const y1 = Math.max(bl[1], br[1], ul[1], ur[1]);2201 this.ctx.fillRect(x0, y0, x1 - x0, y1 - y0);2202 } else {2203 // HACK to draw the gradient onto an infinite rectangle.2204 // PDF gradients are drawn across the entire image while2205 // Canvas only allows gradients to be drawn in a rectangle2206 // The following bug should allow us to remove this.2207 // https://bugzilla.mozilla.org/show_bug.cgi?id=6648842208 this.ctx.fillRect(-1e10, -1e10, 2e10, 2e10);2209 }2210 this.compose(this.current.getClippedPathBoundingBox());2211 this.restore();2212 }2213 // Images2214 beginInlineImage() {2215 unreachable("Should not call beginInlineImage");2216 }2217 beginImageData() {2218 unreachable("Should not call beginImageData");2219 }2220 paintFormXObjectBegin(matrix, bbox) {2221 if (!this.contentVisible) {2222 return;2223 }2224 this.save();2225 this.baseTransformStack.push(this.baseTransform);2226 if (Array.isArray(matrix) && matrix.length === 6) {2227 this.transform.apply(this, matrix);2228 }2229 this.baseTransform = this.ctx.mozCurrentTransform;2230 if (bbox) {2231 const width = bbox[2] - bbox[0];2232 const height = bbox[3] - bbox[1];2233 this.ctx.rect(bbox[0], bbox[1], width, height);2234 this.current.updatePathMinMax(2235 this.ctx.mozCurrentTransform,2236 bbox[0],2237 bbox[1]2238 );2239 this.current.updatePathMinMax(2240 this.ctx.mozCurrentTransform,2241 bbox[2],2242 bbox[3]2243 );2244 this.clip();2245 this.endPath();2246 }2247 }2248 paintFormXObjectEnd() {2249 if (!this.contentVisible) {2250 return;2251 }2252 this.restore();2253 this.baseTransform = this.baseTransformStack.pop();2254 }2255 beginGroup(group) {2256 if (!this.contentVisible) {2257 return;2258 }2259 this.save();2260 // If there's an active soft mask we don't want it enabled for the group, so2261 // clear it out. The mask and suspended canvas will be restored in endGroup.2262 if (this.inSMaskMode) {2263 this.endSMaskMode();2264 this.current.activeSMask = null;2265 }2266 const currentCtx = this.ctx;2267 // TODO non-isolated groups - according to Rik at adobe non-isolated2268 // group results aren't usually that different and they even have tools2269 // that ignore this setting. Notes from Rik on implementing:2270 // - When you encounter an transparency group, create a new canvas with2271 // the dimensions of the bbox2272 // - copy the content from the previous canvas to the new canvas2273 // - draw as usual2274 // - remove the backdrop alpha:2275 // alphaNew = 1 - (1 - alpha)/(1 - alphaBackdrop) with 'alpha' the alpha2276 // value of your transparency group and 'alphaBackdrop' the alpha of the2277 // backdrop2278 // - remove background color:2279 // colorNew = color - alphaNew *colorBackdrop /(1 - alphaNew)2280 if (!group.isolated) {2281 info("TODO: Support non-isolated groups.");2282 }2283 // TODO knockout - supposedly possible with the clever use of compositing2284 // modes.2285 if (group.knockout) {2286 warn("Knockout groups not supported.");2287 }2288 const currentTransform = currentCtx.mozCurrentTransform;2289 if (group.matrix) {2290 currentCtx.transform.apply(currentCtx, group.matrix);2291 }2292 if (!group.bbox) {2293 throw new Error("Bounding box is required.");2294 }2295 // Based on the current transform figure out how big the bounding box2296 // will actually be.2297 let bounds = Util.getAxialAlignedBoundingBox(2298 group.bbox,2299 currentCtx.mozCurrentTransform2300 );2301 // Clip the bounding box to the current canvas.2302 const canvasBounds = [2303 0,2304 0,2305 currentCtx.canvas.width,2306 currentCtx.canvas.height,2307 ];2308 bounds = Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];2309 // Use ceil in case we're between sizes so we don't create canvas that is2310 // too small and make the canvas at least 1x1 pixels.2311 const offsetX = Math.floor(bounds[0]);2312 const offsetY = Math.floor(bounds[1]);2313 let drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);2314 let drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);2315 let scaleX = 1,2316 scaleY = 1;2317 if (drawnWidth > MAX_GROUP_SIZE) {2318 scaleX = drawnWidth / MAX_GROUP_SIZE;2319 drawnWidth = MAX_GROUP_SIZE;2320 }2321 if (drawnHeight > MAX_GROUP_SIZE) {2322 scaleY = drawnHeight / MAX_GROUP_SIZE;2323 drawnHeight = MAX_GROUP_SIZE;2324 }2325 this.current.startNewPathAndClipBox([0, 0, drawnWidth, drawnHeight]);2326 let cacheId = "groupAt" + this.groupLevel;2327 if (group.smask) {2328 // Using two cache entries is case if masks are used one after another.2329 cacheId += "_smask_" + (this.smaskCounter++ % 2);2330 }2331 const scratchCanvas = this.cachedCanvases.getCanvas(2332 cacheId,2333 drawnWidth,2334 drawnHeight,2335 true2336 );2337 const groupCtx = scratchCanvas.context;2338 // Since we created a new canvas that is just the size of the bounding box2339 // we have to translate the group ctx.2340 groupCtx.scale(1 / scaleX, 1 / scaleY);2341 groupCtx.translate(-offsetX, -offsetY);2342 groupCtx.transform.apply(groupCtx, currentTransform);2343 if (group.smask) {2344 // Saving state and cached mask to be used in setGState.2345 this.smaskStack.push({2346 canvas: scratchCanvas.canvas,2347 context: groupCtx,2348 offsetX,2349 offsetY,2350 scaleX,2351 scaleY,2352 subtype: group.smask.subtype,2353 backdrop: group.smask.backdrop,2354 transferMap: group.smask.transferMap || null,2355 startTransformInverse: null, // used during suspend operation2356 });2357 } else {2358 // Setup the current ctx so when the group is popped we draw it at the2359 // right location.2360 currentCtx.setTransform(1, 0, 0, 1, 0, 0);2361 currentCtx.translate(offsetX, offsetY);2362 currentCtx.scale(scaleX, scaleY);2363 currentCtx.save();2364 }2365 // The transparency group inherits all off the current graphics state2366 // except the blend mode, soft mask, and alpha constants.2367 copyCtxState(currentCtx, groupCtx);2368 this.ctx = groupCtx;2369 this.setGState([2370 ["BM", "source-over"],2371 ["ca", 1],2372 ["CA", 1],2373 ]);2374 this.groupStack.push(currentCtx);2375 this.groupLevel++;2376 }2377 endGroup(group) {2378 if (!this.contentVisible) {2379 return;2380 }2381 this.groupLevel--;2382 const groupCtx = this.ctx;2383 const ctx = this.groupStack.pop();2384 this.ctx = ctx;2385 // Turn off image smoothing to avoid sub pixel interpolation which can2386 // look kind of blurry for some pdfs.2387 this.ctx.imageSmoothingEnabled = false;2388 if (group.smask) {2389 this.tempSMask = this.smaskStack.pop();2390 this.restore();2391 } else {2392 this.ctx.restore();2393 const currentMtx = this.ctx.mozCurrentTransform;2394 this.restore();2395 this.ctx.save();2396 this.ctx.setTransform.apply(this.ctx, currentMtx);2397 const dirtyBox = Util.getAxialAlignedBoundingBox(2398 [0, 0, groupCtx.canvas.width, groupCtx.canvas.height],2399 currentMtx2400 );2401 this.ctx.drawImage(groupCtx.canvas, 0, 0);2402 this.ctx.restore();2403 this.compose(dirtyBox);2404 }2405 }2406 beginAnnotations() {2407 this.save();2408 if (this.baseTransform) {2409 this.ctx.setTransform.apply(this.ctx, this.baseTransform);2410 }2411 }2412 endAnnotations() {2413 this.restore();2414 }2415 beginAnnotation(id, rect, transform, matrix, hasOwnCanvas) {2416 this.save();2417 if (Array.isArray(rect) && rect.length === 4) {2418 const width = rect[2] - rect[0];2419 const height = rect[3] - rect[1];2420 if (hasOwnCanvas && this.annotationCanvasMap) {2421 transform = transform.slice();2422 transform[4] -= rect[0];2423 transform[5] -= rect[1];2424 rect = rect.slice();2425 rect[0] = rect[1] = 0;2426 rect[2] = width;2427 rect[3] = height;2428 const [scaleX, scaleY] = Util.singularValueDecompose2dScale(2429 this.ctx.mozCurrentTransform2430 );2431 const { viewportScale } = this;2432 const canvasWidth = Math.ceil(2433 width * this.outputScaleX * viewportScale2434 );2435 const canvasHeight = Math.ceil(2436 height * this.outputScaleY * viewportScale2437 );2438 this.annotationCanvas = this.canvasFactory.create(2439 canvasWidth,2440 canvasHeight2441 );2442 const { canvas, context } = this.annotationCanvas;2443 const viewportScaleFactorStr = `var(--zoom-factor) * ${PixelsPerInch.PDF_TO_CSS_UNITS}`;2444 canvas.style.width = `calc(${width}px * ${viewportScaleFactorStr})`;2445 canvas.style.height = `calc(${height}px * ${viewportScaleFactorStr})`;2446 this.annotationCanvasMap.set(id, canvas);2447 this.annotationCanvas.savedCtx = this.ctx;2448 this.ctx = context;2449 this.ctx.setTransform(scaleX, 0, 0, -scaleY, 0, height * scaleY);2450 addContextCurrentTransform(this.ctx);2451 resetCtxToDefault(this.ctx);2452 } else {2453 resetCtxToDefault(this.ctx);2454 this.ctx.rect(rect[0], rect[1], width, height);2455 this.clip();2456 this.endPath();2457 }2458 }2459 this.current = new CanvasExtraState(2460 this.ctx.canvas.width,2461 this.ctx.canvas.height2462 );2463 this.transform.apply(this, transform);2464 this.transform.apply(this, matrix);2465 }2466 endAnnotation() {2467 if (this.annotationCanvas) {...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require('webpagetest');2var wpt = new WebPageTest('www.webpagetest.org');3wpt.resetCtxToDefault();4var wpt = require('webpagetest');5var wpt = new WebPageTest('www.webpagetest.org');6wpt.resetCtxToDefault();7var wpt = require('webpagetest');8var wpt = new WebPageTest('www.webpagetest.org');9wpt.resetCtxToDefault();10var wpt = require('webpagetest');11var wpt = new WebPageTest('www.webpagetest.org');12wpt.resetCtxToDefault();13var wpt = require('webpagetest');14var wpt = new WebPageTest('www.webpagetest.org');15wpt.resetCtxToDefault();16var wpt = require('webpagetest');17var wpt = new WebPageTest('www.webpagetest.org');18wpt.resetCtxToDefault();19var wpt = require('webpagetest');20var wpt = new WebPageTest('www.webpagetest.org');21wpt.resetCtxToDefault();22var wpt = require('webpagetest');23var wpt = new WebPageTest('www.webpagetest.org');24wpt.resetCtxToDefault();25var wpt = require('webpagetest');26var wpt = new WebPageTest('www.webpagetest.org');27wpt.resetCtxToDefault();28var wpt = require('webpagetest');29var wpt = new WebPageTest('www.webpagetest.org');30wpt.resetCtxToDefault();31var wpt = require('webpagetest');32var wpt = new WebPageTest('www.webpagetest.org');33wpt.resetCtxToDefault();

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptDriver = require('wptdriver');2var driver = wptDriver.create();3driver.resetCtxToDefault();4var wptDriver = require('wptdriver');5var driver = wptDriver.create();6driver.resetCtxToDefault();7var wptDriver = require('wptdriver');8var driver = wptDriver.create();9driver.resetCtxToDefault();10var wptDriver = require('wptdriver');11var driver = wptDriver.create();12driver.resetCtxToDefault();13var wptDriver = require('wptdriver');14var driver = wptDriver.create();15driver.resetCtxToDefault();16var wptDriver = require('wptdriver');17var driver = wptDriver.create();18driver.resetCtxToDefault();19var wptDriver = require('wptdriver');20var driver = wptDriver.create();21driver.resetCtxToDefault();22var wptDriver = require('wptdriver');23var driver = wptDriver.create();24driver.resetCtxToDefault();25var wptDriver = require('wptdriver');26var driver = wptDriver.create();27driver.resetCtxToDefault();28var wptDriver = require('wptdriver');29var driver = wptDriver.create();30driver.resetCtxToDefault();31var wptDriver = require('wptdriver');32var driver = wptDriver.create();33driver.resetCtxToDefault();34var wptDriver = require('wptdriver');35var driver = wptDriver.create();36driver.resetCtxToDefault();37var wptDriver = require('wptdriver');38var driver = wptDriver.create();39driver.resetCtxToDefault();

Full Screen

Using AI Code Generation

copy

Full Screen

1var wpt = require("webpagetest");2var options = {3};4var test = wpt(options);5 if (err) {6 console.log(err);7 } else {8 console.log(JSON.stringify(data));9 test.getTestResults(data.data.testId, function (err, data) {10 if (err) {11 console.log(err);12 } else {13 console.log(JSON.stringify(data));14 }15 });16 }17});18var wpt = require("webpagetest");19var options = {20};21var test = wpt(options);22 if (err) {23 console.log(err);24 } else {25 console.log(JSON.stringify(data));26 test.getTestResults(data.data.testId, function (err, data) {27 if (err) {28 console.log(err);29 } else {30 console.log(JSON.stringify(data));31 }32 });33 }34});35var wpt = require("webpagetest");36var options = {37};38var test = wpt(options);39 if (err) {40 console.log(err);41 } else {42 console.log(JSON.stringify(data));43 test.getTestResults(data.data.testId, function (err, data) {44 if (err) {45 console.log(err);46 } else {47 console.log(JSON.stringify(data));48 }49 });50 }51});52var wpt = require("webpagetest");53var options = {

Full Screen

Using AI Code Generation

copy

Full Screen

1wptbBuilder.resetCtxToDefault();2wptbBuilder.resetCtxToDefault();3wptbBuilder.resetCtxToDefault();4wptbBuilder.resetCtxToDefault();5wptbBuilder.resetCtxToDefault();6wptbBuilder.resetCtxToDefault();7wptbBuilder.resetCtxToDefault();8wptbBuilder.resetCtxToDefault();9wptbBuilder.resetCtxToDefault();10wptbBuilder.resetCtxToDefault();11wptbBuilder.resetCtxToDefault();12wptbBuilder.resetCtxToDefault();

Full Screen

Using AI Code Generation

copy

Full Screen

1var wptDriver = require('wpt-driver');2var driver = wptDriver.createDriver();3driver.resetCtxToDefault();4driver.quit();5var driver = require('wpt-driver').createDriver();6driver.quit();7var driver = require('wpt-driver').createDriver();8driver.quit();9I have a question about the WPT Driver. I am trying to run a script on my mac (10.10.5) using the latest version of the driver (2.0.0). I have installed the driver and the dependencies using npm (using sudo npm install -g wpt-driver

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run wpt automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful