From 376a3aed11b3581a69ee9e1c9956d2952b64705b Mon Sep 17 00:00:00 2001 From: flyskyko Date: Thu, 22 Apr 2021 12:21:03 +0900 Subject: [PATCH 1/2] add context2d setLineDash() and lineDashOffset. --- src/jspdf.js | 2 +- src/libs/FileSaver.js | 3 +- src/libs/pdfname.js | 7 +++- src/modules/cell.js | 5 ++- src/modules/context2d.js | 68 +++++++++++++++++++++++++++++++ test/reference/lineDash.pdf | Bin 0 -> 3594 bytes test/specs/context2d.spec.js | 75 +++++++++++++++++++++++++++++++++++ 7 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 test/reference/lineDash.pdf diff --git a/src/jspdf.js b/src/jspdf.js index ee3ba1034..94f3a5f69 100644 --- a/src/jspdf.js +++ b/src/jspdf.js @@ -2001,7 +2001,7 @@ function jsPDF(options) { font: font, out: out, newObject: newObject, - putStream: putStream, + putStream: putStream }); if (font.isAlreadyPutted !== true) { diff --git a/src/libs/FileSaver.js b/src/libs/FileSaver.js index ad1d302d1..89586ea36 100644 --- a/src/libs/FileSaver.js +++ b/src/libs/FileSaver.js @@ -90,7 +90,8 @@ var saveAs = /* noop */ } : // Use download attribute first if possible (#193 Lumia mobile) unless this is a native app - (typeof HTMLAnchorElement !== "undefined" && "download" in HTMLAnchorElement.prototype) + typeof HTMLAnchorElement !== "undefined" && + "download" in HTMLAnchorElement.prototype ? function saveAs(blob, name, opts) { var URL = _global.URL || _global.webkitURL; var a = document.createElement("a"); diff --git a/src/libs/pdfname.js b/src/libs/pdfname.js index 8b6b3e6ca..9987d35f8 100644 --- a/src/libs/pdfname.js +++ b/src/libs/pdfname.js @@ -5,8 +5,11 @@ */ function toPDFName(str) { // eslint-disable-next-line no-control-regex - if(/[^\u0000-\u00ff]/.test(str)){ // non ascii string - throw new Error('Invalid PDF Name Object: ' + str + ', Only accept ASCII characters.'); + if (/[^\u0000-\u00ff]/.test(str)) { + // non ascii string + throw new Error( + "Invalid PDF Name Object: " + str + ", Only accept ASCII characters." + ); } var result = "", strLength = str.length; diff --git a/src/modules/cell.js b/src/modules/cell.js index 1c69dbaa8..fce190739 100644 --- a/src/modules/cell.js +++ b/src/modules/cell.js @@ -442,7 +442,10 @@ import { jsPDF } from "../jspdf.js"; }); } - if (autoSize || (Array.isArray(headers) && typeof headers[0] === "string")) { + if ( + autoSize || + (Array.isArray(headers) && typeof headers[0] === "string") + ) { var headerName; for (i = 0; i < headerNames.length; i += 1) { headerName = headerNames[i]; diff --git a/src/modules/context2d.js b/src/modules/context2d.js index eb7edee14..fdba25141 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -51,6 +51,8 @@ import { this.currentPoint = ctx.currentPoint || new Point(); this.miterLimit = ctx.miterLimit || 10.0; this.lastPoint = ctx.lastPoint || new Point(); + this.lineDashOffset = ctx.lineDashOffset || 0.0; + this.lineDash = ctx.lineDash || []; this.ignoreClearRect = typeof ctx.ignoreClearRect === "boolean" ? ctx.ignoreClearRect : true; @@ -641,6 +643,32 @@ import { } }); + /** + * A float specifying the amount of the line dash offset. The default value is 0.0. + * + * @name lineDashOffset + * @default 0.0 + */ + Object.defineProperty(this, "lineDashOffset", { + get: function() { + return this.ctx.lineDashOffset; + }, + set: function(value) { + this.ctx.lineDashOffset = value; + setLineDash.call(this); + } + }); + + Object.defineProperty(this, "lineDash", { + get: function() { + return this.ctx.lineDash; + }, + set: function(value) { + this.ctx.lineDash = value; + setLineDash.call(this); + } + }); + // Not HTML API Object.defineProperty(this, "ignoreClearRect", { get: function() { @@ -652,6 +680,16 @@ import { }); }; + /** + * Sets the line dash pattern used when stroking lines. + * @name setLineDash + * @function + * @description It uses an array of values that specify alternating lengths of lines and gaps which describe the pattern. + */ + Context2D.prototype.setLineDash = function(dashArray) { + this.lineDash = dashArray; + }; + Context2D.prototype.fill = function() { pathPreProcess.call(this, "fill", false); }; @@ -1105,6 +1143,8 @@ import { this.lineCap = this.ctx.lineCap; this.lineWidth = this.ctx.lineWidth; this.lineJoin = this.ctx.lineJoin; + this.lineDash = this.ctx.lineDash; + this.lineDashOffset = this.ctx.lineDashOffset; } }; @@ -2383,4 +2423,32 @@ import { Math.round(maxy - miny) ); }; + + var getPrevLineDashValue = function(lineDash, lineDashOffset) { + return JSON.stringify({ + lineDash: lineDash, + lineDashOffset: lineDashOffset + }); + }; + + var setLineDash = function() { + // Avoid unnecessary line dash declarations. + if ( + !this.prevLineDash && + !this.ctx.lineDash.length && + !this.ctx.lineDashOffset + ) { + return; + } + + // Avoid unnecessary line dash declarations. + const nextLineDash = getPrevLineDashValue( + this.ctx.lineDash, + this.ctx.lineDashOffset + ); + if (this.prevLineDash !== nextLineDash) { + this.pdf.setLineDash(this.ctx.lineDash, this.ctx.lineDashOffset); + this.prevLineDash = nextLineDash; + } + }; })(jsPDF.API); diff --git a/test/reference/lineDash.pdf b/test/reference/lineDash.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e86d507021673acc4865115ea25fcd23aed2a907 GIT binary patch literal 3594 zcmcH+*>2iE@O{5xUZS?Df_E2uRVY#eA)$vzAyTWNJQ&yzN5)Reor{C>ZtqYun(W}m&+orj6F5YZ8`f?c7Z0}zjB{_U7(QbaY7qF} zAv1<5iI(wHB_K1xdo}aDt#KZd(;C8Me}br+D0c8rtIgc80AaKVaO* zVf@B(f=@AoZ?aQ!xOqGrb|tJiwo-eyj+M%I}-~@$XZ;rTI9qC}N^i}-yF%Z;ji~gB)>&-;WZSN@) z#JYqYtAxUVy~SV92--ae`}1QxIUkF~g}+eA;V@cw;U1EqJWT(b44M7?GzGD}DQ4#T z+6(;Y9?rkLF|NDO)}Ab=7Acg3Crx*39+K;a>%1#|?0OrQoz zkcG=u4%(1*7NRL_2rcX870bx~rs&>`VH9cn*^*U3KfH<{O-an`eiz2jPNo0yINF8f Vl5!@|UaZrMX>wt+gM*Vn?>B?0z6SsR literal 0 HcmV?d00001 diff --git a/test/specs/context2d.spec.js b/test/specs/context2d.spec.js index 669f5af17..e1e39a0fe 100644 --- a/test/specs/context2d.spec.js +++ b/test/specs/context2d.spec.js @@ -485,6 +485,81 @@ describe("Context2D: standard tests", () => { comparePdf(doc.output(), "moveTo_lineTo_stroke_fill.pdf", "context2d"); }); + it("context2d: setLineDash(), lineDashOffset", () => { + var doc = new jsPDF({ + orientation: "p", + unit: "pt", + format: "a4", + floatPrecision: 2 + }); + var ctx = doc.context2d; + + var y = 20; + var pad = 20; + + ctx.lineWidth = 5; + ctx.beginPath(); + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.save(); + ctx.beginPath(); + ctx.setLineDash([10, 20]); + ctx.lineDashOffset = 10; + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.beginPath(); + ctx.setLineDash([]); + ctx.lineDashOffset = 0; + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.beginPath(); + ctx.setLineDash([10, 20]); + ctx.lineDashOffset = 10; + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.save(); + ctx.beginPath(); + ctx.setLineDash([]); + ctx.lineDashOffset = 0; + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.restore(); + ctx.beginPath(); + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.save(); + ctx.beginPath(); + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.restore(); + ctx.beginPath(); + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + y += pad; + ctx.restore(); + ctx.beginPath(); + ctx.moveTo(20, y); + ctx.lineTo(200, y); + ctx.stroke(); + + comparePdf(doc.output(), "lineDash.pdf", "context2d"); + }); + it("context2d: textBaseline", () => { var doc = new jsPDF({ orientation: "p", From 7955ddab367b3dc6c6e6dd869dcc92819d55c668 Mon Sep 17 00:00:00 2001 From: flyskyko Date: Tue, 25 May 2021 22:13:15 +0900 Subject: [PATCH 2/2] add context2d getLineDash() --- src/modules/context2d.js | 17 +++++++++++++++++ test/specs/context2d.spec.js | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/modules/context2d.js b/src/modules/context2d.js index fdba25141..2d30c8ea5 100644 --- a/src/modules/context2d.js +++ b/src/modules/context2d.js @@ -659,6 +659,7 @@ import { } }); + // Not HTML API Object.defineProperty(this, "lineDash", { get: function() { return this.ctx.lineDash; @@ -690,6 +691,22 @@ import { this.lineDash = dashArray; }; + /** + * gets the current line dash pattern. + * @name getLineDash + * @function + * @returns {Array} An Array of numbers that specify distances to alternately draw a line and a gap (in coordinate space units). If the number, when setting the elements, is odd, the elements of the array get copied and concatenated. For example, setting the line dash to [5, 15, 25] will result in getting back [5, 15, 25, 5, 15, 25]. + */ + Context2D.prototype.getLineDash = function() { + if (this.lineDash.length % 2) { + // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/getLineDash#return_value + return this.lineDash.concat(this.lineDash); + } else { + // The copied value is returned to prevent contamination from outside. + return this.lineDash.slice(); + } + }; + Context2D.prototype.fill = function() { pathPreProcess.call(this, "fill", false); }; diff --git a/test/specs/context2d.spec.js b/test/specs/context2d.spec.js index e1e39a0fe..e55810e01 100644 --- a/test/specs/context2d.spec.js +++ b/test/specs/context2d.spec.js @@ -560,6 +560,24 @@ describe("Context2D: standard tests", () => { comparePdf(doc.output(), "lineDash.pdf", "context2d"); }); + it("context2d: getLineDash()", () => { + var doc = new jsPDF({ + orientation: "p", + unit: "pt", + format: "a4", + floatPrecision: 2 + }); + var ctx = doc.context2d; + + expect(ctx.getLineDash()).toEqual([]); + + ctx.setLineDash([1, 2]); + expect(ctx.getLineDash()).toEqual([1, 2]); + + ctx.setLineDash([1, 2, 3]); + expect(ctx.getLineDash()).toEqual([1, 2, 3, 1, 2, 3]); + }); + it("context2d: textBaseline", () => { var doc = new jsPDF({ orientation: "p",