返回 aippt
ppt2svg.js
根目录 / static / ppt2svg.js
1 /* eslint-disable no-unused-vars */
2 // import { geometryPaths } from './geometry.js'
3 // import { drawChart } from './chart.js'
4
5 function D3Element(element) {
6 this.attr = function (k, v) {
7 if (v == undefined) {
8 return element.getAttribute(k)
9 } else {
10 element.setAttribute(k, v)
11 return this
12 }
13 }
14 this.style = function (k, v) {
15 if (v == undefined) {
16 return element.style[k]
17 } else {
18 element.style[k] = v
19 return this
20 }
21 }
22 this.text = function (v) {
23 if (v == undefined) {
24 return element.textContent
25 } else {
26 element.textContent = v
27 return this
28 }
29 }
30 this.append = function (tag) {
31 let node = document.createElementNS('http://www.w3.org/2000/svg', tag)
32 element.appendChild(node)
33 return new D3Element(node)
34 }
35 this.parent = function () {
36 return element.parentNode ? new D3Element(element.parentNode) : null
37 }
38 this.node = function () {
39 return element
40 }
41 this.html = function (v) {
42 if (v == undefined) {
43 return element.innerHTML
44 } else {
45 element.innerHTML = v
46 return this
47 }
48 }
49 this.translate = function (x, y, start) {
50 let transform = element.getAttribute('transform')
51 if (x == undefined && y == undefined) {
52 x = y = 0
53 if (transform) {
54 let idx = transform.indexOf('translate')
55 if (idx > -1) {
56 let arr = transform.substring(idx + 10, transform.indexOf(')', idx + 10)).replace('(', '').split(',')
57 x = parseFloat(arr[0] || 0)
58 y = parseFloat(arr[1] || 0)
59 }
60 }
61 return { x, y }
62 } else {
63 if (y == undefined) {
64 y = 0
65 }
66 if (transform) {
67 let idx = transform.indexOf('translate')
68 if (idx > -1) {
69 let eIdx = transform.indexOf(')', idx + 10)
70 transform = transform.substring(0, idx) + `translate(${x}, ${y})` + transform.substring(eIdx + 1)
71 element.setAttribute('transform', transform.trim())
72 } else {
73 element.setAttribute('transform', start ? `translate(${x}, ${y}) ${transform}`: `${transform} translate(${x}, ${y})`)
74 }
75 } else {
76 element.setAttribute('transform', `translate(${x}, ${y})`)
77 }
78 return this
79 }
80 }
81 this.scale = function (x, y, start) {
82 let transform = element.getAttribute('transform')
83 if (x == undefined && y == undefined) {
84 x = y = 1
85 if (transform) {
86 let idx = transform.indexOf('scale')
87 if (idx > -1) {
88 let arr = transform.substring(idx + 6, transform.indexOf(')', idx + 6)).replace('(', '').split(',')
89 x = parseFloat(arr[0] || 0)
90 y = arr[1] == undefined ? x : parseFloat(arr[1])
91 }
92 }
93 return { x, y }
94 } else {
95 if (y == undefined) {
96 y = x
97 }
98 if (transform) {
99 let idx = transform.indexOf('scale')
100 if (idx > -1) {
101 let eIdx = transform.indexOf(')', idx + 6)
102 transform = transform.substring(0, idx) + `scale(${x}, ${y})` + transform.substring(eIdx + 1)
103 element.setAttribute('transform', transform.trim())
104 } else {
105 element.setAttribute('transform', start ? `scale(${x}, ${y}) ${transform}` : `${transform} scale(${x}, ${y})`)
106 }
107 } else {
108 element.setAttribute('transform', `scale(${x}, ${y})`)
109 }
110 return this
111 }
112 }
113 this.rotate = function (rotate, x, y, start) {
114 let transform = element.getAttribute('transform')
115 if (rotate == undefined) {
116 x = y = 0
117 if (transform) {
118 let idx = transform.indexOf('rotate')
119 if (idx > -1) {
120 let arr = transform.substring(idx + 7, transform.indexOf(')', idx + 7)).replace('(', '').split(',')
121 rotate = parseFloat(arr[0] || 0)
122 x = arr[1] == undefined ? 0 : parseFloat(arr[1])
123 y = arr[2] == undefined ? 0 : parseFloat(arr[2])
124 }
125 }
126 return { rotate, x, y }
127 } else {
128 let transformRotate = rotate ? `rotate(${rotate})` : ''
129 if (x != undefined && y != undefined) {
130 transformRotate = `rotate(${rotate}, ${x}, ${y})`
131 }
132 if (transform) {
133 let idx = transform.indexOf('rotate')
134 if (idx > -1) {
135 let eIdx = transform.indexOf(')', idx + 7)
136 transform = transform.substring(0, idx) + transformRotate + transform.substring(eIdx + 1)
137 element.setAttribute('transform', transform.trim())
138 } else {
139 element.setAttribute('transform', start ? `${transformRotate} ${transform}` : `${transform} ${transformRotate}`)
140 }
141 } else {
142 element.setAttribute('transform', transformRotate)
143 }
144 return this
145 }
146 }
147 }
148
149 const d3 = {
150 select: function (o) {
151 return new D3Element((typeof o == 'string') ? document.querySelector(o) : o)
152 },
153 addEventListener: function (type, event, fun) {
154 if (type === 'window') {
155 window.addEventListener(event, fun)
156 } else if (type === 'document') {
157 document.addEventListener(event, fun)
158 }
159 }
160 }
161
162 function Ppt2Svg(_svg, svgWidth, svgHeight) {
163 var pptx = null
164 var page = null
165 var imageCache = {}
166 var pageIndex = 0
167 var ctx = {}
168 var idMap = {}
169 var counter = 0
170 var zoom = 1
171 var defs = null
172 var mode = 'view'
173 var pointList = []
174 var mTimer = null
175 var currentPoint = null
176 var currentSelect = null
177 // eslint-disable-next-line @typescript-eslint/no-this-alias
178 const $this = this
179 const svg = d3.select((typeof _svg == 'string') ? ('#' + _svg) : _svg)
180 .attr('width', svgWidth || 960)
181 .attr('height', svgHeight || 540)
182
183 this.drawPptx = (pptxObj, pageIdx, selectElementId) => {
184 removePoint()
185 removeElementMoveScale()
186 ctx = {}
187 idMap = {}
188 imageCache = {}
189 counter = 0
190 pptx = pptxObj
191 pageIndex = pageIdx
192 zoom = svgWidth / pptx.width
193 svg.html('')
194 defs = svg.append('defs')
195 page = pptxObj.pages[pageIdx]
196 let placeholder = {}
197 let slideMasterIdx = page.extInfo.slideMasterIdx
198 if (slideMasterIdx != null && pptxObj.slideMasters) {
199 let slideMaster = pptxObj.slideMasters[slideMasterIdx]
200 let slideLayoutIdx = page.extInfo.slideLayoutIdx
201 let slideLayout = slideLayoutIdx != null ? slideMaster.slideLayouts[slideLayoutIdx] : null
202 drawBackground(page.extInfo.background || (slideLayout || {}).background || slideMaster.background)
203 drawSlideMaster(slideMaster, slideLayout, placeholder)
204 if (slideLayout) {
205 drawSlideLayout(slideLayout, placeholder)
206 }
207 } else {
208 drawBackground(page.extInfo.background)
209 }
210 if (!page.children) {
211 return
212 }
213 recursion(page.children, obj => {
214 idMap[obj.id] = obj
215 if (obj.extInfo.property && obj.extInfo.property.placeholder) {
216 // 继承母版占位符
217 let element_property = obj.extInfo.property
218 let placeholder_property = placeholder[element_property.placeholder.type] || {}
219 for (let k in placeholder_property) {
220 if (element_property[k] == null) {
221 element_property[k] = placeholder_property[k]
222 }
223 }
224 }
225 })
226 for (let i = 0; i < page.children.length; i++) {
227 drawElement(page.children[i])
228 }
229 recursion(page.children, obj => {
230 idMap[obj.id] = obj
231 })
232 calcPointList(page)
233 if (selectElementId) {
234 addElementMoveScale(idMap[selectElementId])
235 }
236 }
237
238 this.svgNode = () => {
239 return svg.node()
240 }
241
242 this.resetSize = (_svgWidth, _svgHeight) => {
243 svgWidth = _svgWidth || svgWidth
244 svgHeight = _svgHeight || svgHeight
245 svg.attr('width', svgWidth).attr('height', svgHeight)
246 if (pptx) {
247 zoom = svgWidth / pptx.width
248 this.drawPptx(pptx, pageIndex)
249 }
250 }
251
252 this.setMode = (_mode) => {
253 // view / edit
254 mode = _mode || 'view'
255 return mode
256 }
257
258 function scaleAnchor(point) {
259 return point ? [...point.map(s => s * zoom)] : point
260 }
261
262 function scaleValue(value) {
263 return value ? value * zoom : value
264 }
265
266 function shapeHandle(property, parent) {
267 let g = parent.append('g')
268 let anchor = scaleAnchor(property.anchor)
269 if (!anchor) {
270 g.attr('groupFlipX', ctx.groupFlipX || 1)
271 g.attr('groupFlipY', ctx.groupFlipY || 1)
272 return g
273 }
274 let transform = []
275 let cx = anchor[0] + anchor[2] / 2
276 let cy = anchor[1] + anchor[3] / 2
277 transform.push(`rotate(${property.rotation || 0}, ${cx}, ${cy})`)
278 if (property.rotation) {
279 if (property.realType == 'Group') {
280 ctx.groupRotation = (ctx.groupRotation || 0) + property.rotation
281 }
282 }
283 if (property.flipVertical) {
284 transform.push(`translate(${cx}, ${cy})`)
285 transform.push(`scale(1, -1)`)
286 transform.push(`translate(${-cx}, ${-cy})`)
287 if (property.realType == 'Group') {
288 ctx.groupFlipY = -(ctx.groupFlipY || 1)
289 }
290 }
291 if (property.flipHorizontal) {
292 transform.push(`translate(${cx}, ${cy})`)
293 transform.push(`scale(-1, 1)`)
294 transform.push(`translate(${-cx}, ${-cy})`)
295 if (property.realType == 'Group') {
296 ctx.groupFlipX = -(ctx.groupFlipX || 1)
297 }
298 }
299 // 嵌套容器 Group
300 let interior = scaleAnchor(property.interiorAnchor)
301 if (interior && interior.length > 0) {
302 let scaleX = interior[2] == 0 || anchor[2] == interior[2] ? 1 : anchor[2] / interior[2]
303 let scaleY = interior[3] == 0 || anchor[3] == interior[3] ? 1 : anchor[3] / interior[3]
304 let tx = anchor[0] - interior[0] * scaleX
305 let ty = anchor[1] - interior[1] * scaleY
306 transform.push(`translate(${tx}, ${ty})`)
307 ctx._interior = ctx.interior
308 ctx.interior = { scaleX, scaleY, tx, ty }
309 } else if (property.realType == 'Group') {
310 ctx._interior = ctx.interior
311 ctx.interior = { scaleX: 1, scaleY: 1, tx: 0, ty: 0 }
312 } else if (ctx.interior) {
313 g.attr('interior', JSON.stringify(ctx.interior))
314 }
315 if (transform.length > 0) {
316 g.attr('transform', transform.join(' '))
317 }
318 g.attr('groupFlipX', ctx.groupFlipX || 1)
319 g.attr('groupFlipY', ctx.groupFlipY || 1)
320 return g
321 }
322
323 function drawBackground(background) {
324 if (!background) {
325 ctx.bgAnchor = null
326 ctx.bgFillStyle = null
327 return
328 }
329 let anchor = scaleAnchor(background.anchor)
330 ctx.bgAnchor = anchor
331 ctx.bgFillStyle = background.fillStyle
332 let fill = toPaint(background.fillStyle, anchor)
333 svg.append('rect')
334 .attr('x', 0).attr('y', 0)
335 .attr('width', anchor[2])
336 .attr('height', anchor[3])
337 .attr('fill', fill)
338 .node().addEventListener('click', function (e) {
339 if (!currentPoint) {
340 removeElementMoveScale()
341 }
342 })
343 }
344
345 function drawSlideMaster(slideMaster, slideLayout, placeholder) {
346 recursion(slideMaster.children, obj => {
347 if (obj.extInfo.property && obj.extInfo.property.placeholder) {
348 obj.noDraw = true
349 placeholder[obj.extInfo.property.placeholder.type] = obj.extInfo.property
350 }
351 })
352 if (slideLayout && slideLayout.noMaster) {
353 return
354 }
355 for (let i = 0; slideMaster.children && i < slideMaster.children.length; i++) {
356 drawElement(slideMaster.children[i])
357 }
358 }
359
360 function drawSlideLayout(slideLayout, placeholder) {
361 recursion(slideLayout.children, obj => {
362 if (obj.extInfo.property && obj.extInfo.property.placeholder) {
363 obj.noDraw = true
364 placeholder[obj.extInfo.property.placeholder.type] = obj.extInfo.property
365 }
366 })
367 for (let i = 0; slideLayout.children && i < slideLayout.children.length; i++) {
368 drawElement(slideLayout.children[i])
369 }
370 }
371
372 function recursion(children, fn) {
373 if (!children) {
374 return
375 }
376 for (let i = 0; i < children.length; i++) {
377 let c = children[i]
378 fn(c)
379 if (c.children && c.children.length > 0) {
380 recursion(c.children, fn)
381 }
382 }
383 }
384
385 function drawElement(obj, parent) {
386 if (obj.noDraw || !obj.extInfo.property.anchor || obj.extInfo.property.hidden) {
387 // console.log('ignore element:', obj)
388 return
389 }
390 if (!parent) {
391 parent = svg
392 }
393 if (obj.type == 'text' || obj.type == 'freeform') {
394 drawText(obj, parent)
395 } else if (obj.type == 'image') {
396 drawImage(obj, parent)
397 } else if (obj.type == 'diagram') {
398 drawDiagram(obj, parent)
399 } else if (obj.type == 'container') {
400 drawContainer(obj, parent)
401 } else if (obj.type == 'table') {
402 drawTable(obj, parent)
403 } else if (obj.type == 'connector') {
404 drawConnector(obj, parent)
405 } else if (obj.type == 'graphicFrame') {
406 drawGraphicFrame(obj, parent)
407 }
408 }
409
410 this.redrawElementWithId = (id) => {
411 let obj = idMap[id]
412 if (obj.noDraw || !obj.extInfo.property.anchor || obj.extInfo.property.hidden) {
413 return
414 }
415 let parent = idMap[obj.pid]
416 while (parent) {
417 if (parent.type == 'container') {
418 // 重新渲染页面
419 this.drawPptx(pptx, pageIndex, id)
420 return
421 }
422 parent = idMap[parent.pid]
423 }
424 let list = []
425 if (obj.point) {
426 let x = obj.point[0]
427 let y = obj.point[1]
428 let endX = obj.point[0] + obj.point[2]
429 let endY = obj.point[1] + obj.point[3]
430 for (let i = 0; i < pointList.length; i++) {
431 let point = pointList[i]
432 if (point.obj.id == id) {
433 break
434 }
435 if (point.x >= x && point.y >= y && point.endX <= endX && point.endY <= endY) {
436 list.push(point.obj)
437 } else if (point.y > y && point.y < endY && (point.x > x && point.x < endX || point.endX > x && point.endX < endX)) {
438 list.push(point.obj)
439 } else if (point.x > x && point.x < endX && (point.y > y && point.y < endY || point.endY > y && point.endY < endY)) {
440 list.push(point.obj)
441 }
442 if (list.length > 0) {
443 // 重新渲染页面
444 this.drawPptx(pptx, pageIndex, id)
445 return
446 }
447 }
448 }
449 let element = document.getElementById('g-' + obj.id)
450 if (element) {
451 element.remove()
452 let parent = element.parentNode ? d3.select(element.parentNode) : null
453 drawElement(obj, parent)
454 }
455 page && calcPointList(page)
456 if (currentSelect) {
457 addElementMoveScale(currentSelect)
458 }
459 }
460
461 function drawText(obj, parent) {
462 let property = obj.extInfo.property
463 let geometryName = (property.geometry || {}).name || 'rect'
464 let g = shapeHandle(property, parent)
465 g.attr('id', 'g-' + obj.id)
466 addElementEvent(g, obj)
467 if (geometryName == 'tableColumn') {
468 drawTableColumn(property, g)
469 } else {
470 drawGeometry(property, g)
471 }
472 if (!obj.children || obj.children.length == 0) {
473 return
474 }
475 drawTextWithG(g, obj)
476 }
477
478 function drawTextWithG(g, obj) {
479 let childNodes = g.node().childNodes
480 for (let i = childNodes.length - 1; i >= 0; i--) {
481 if (childNodes[i].tagName == 'text') {
482 childNodes[i].remove()
483 }
484 }
485 if (!obj.children || obj.children.length == 0) {
486 return
487 }
488 let property = obj.extInfo.property
489 let anchor = scaleAnchor(property.anchor)
490 let wordWrap = property.textWordWrap ?? true
491 let textInsets = property.textInsets || [0, 0, 0, 0]
492 let isVertical = property.textDirection && property.textDirection.indexOf('VERTICAL') > -1
493 let verticalAlignment = property.textVerticalAlignment
494 let textNode = g.append('text').attr('id', obj.id).attr('x', anchor[0]).attr('y', anchor[1])
495 textNode.style('user-select', 'none')
496 if (isVertical) {
497 textNode.style('writing-mode', 'vertical-rl')
498 }
499 let cx = anchor[0] + anchor[2] / 2
500 let cy = anchor[1] + anchor[3] / 2
501 let x = parseInt(g.attr('groupFlipX') || ctx.groupFlipX || 1)
502 let y = parseInt(g.attr('groupFlipY') || ctx.groupFlipX || 1)
503 let transform = []
504 if (property.flipHorizontal) {
505 x *= -1
506 }
507 if (property.flipVertical) {
508 y *= -1
509 }
510 if (y == -1) {
511 if (x == 1) {
512 transform.push(`translate(${cx}, ${cy})`)
513 transform.push(`scale(-1, 1)`)
514 transform.push(`translate(${-cx}, ${-cy})`)
515 }
516 } else if (x == -1) {
517 transform.push(`translate(${cx}, ${cy})`)
518 transform.push(`scale(-1, 1)`)
519 transform.push(`translate(${-cx}, ${-cy})`)
520 }
521 if (transform.length > 0) {
522 textNode.attr('transform', transform.join(' '))
523 }
524 let marginTop = property.strokeStyle ? scaleValue(property.strokeStyle.lineWidth || 1) : 0
525 let maxFontSize = 0
526 let maxWidth = anchor[2] - textInsets[1] - textInsets[3]
527 for (let i = 0; i < obj.children.length; i++) {
528 let p = obj.children[i]
529 let p_property = p.extInfo.property
530 let textAlign = p_property.textAlign
531 let lineSpacing = p_property.lineSpacing > 0 ? (p_property.lineSpacing / 100) : (1 + Math.abs(p_property.lineSpacing || 0) / 100)
532 let createPNode = () => {
533 let pNode = textNode.append('tspan').attr('id', p.id)
534 if (textAlign == 'CENTER') {
535 pNode.attr('x', anchor[0] + (anchor[2] - textInsets[1] - textInsets[3]) / 2 + textInsets[1])
536 pNode.attr('text-anchor', 'middle')
537 } else if (textAlign == 'RIGHT') {
538 pNode.attr('x', anchor[0] + anchor[2] - textInsets[3])
539 pNode.attr('text-anchor', 'end')
540 } else {
541 pNode.attr('x', anchor[0] + textInsets[1])
542 pNode.attr('text-anchor', 'start')
543 }
544 return pNode
545 }
546 let pNode = createPNode()
547 for (let i = 0; i < p.children.length; i++) {
548 let r = p.children[i]
549 let r_property = r.extInfo.property
550 if (r_property.slideNum) {
551 r.text = (pageIndex + 1) + ''
552 }
553 let fontSize = scaleValue(r_property.fontSize || 16)
554 let createRNode = (_pNode) => {
555 let rNode = _pNode.append('tspan').attr('id', r.id)
556 if (r_property.bold) {
557 rNode.style('font-weight', 'bold')
558 }
559 if (r_property.italic) {
560 rNode.style('font-style', 'italic')
561 }
562 if (r_property.underlined) {
563 rNode.style('text-decoration', 'underline')
564 }
565 rNode.style('cursor', 'text')
566 rNode.style('font-size', fontSize + 'px')
567 rNode.style('font-family', (r_property.fontFamily || '等线'))
568 let filter = toShadow(r_property.shadow, anchor)
569 if (filter) {
570 rNode.attr('filter', filter)
571 }
572 if (r_property.line && (r_property.fontColor == null || r_property.fontColor.type == 'noFill')) {
573 let fillStyle = toPaint(r_property.line.paint, anchor)
574 rNode.attr('fill', 'none')
575 rNode.attr('stroke', fillStyle)
576 rNode.attr('stroke-width', scaleValue(r_property.line.lineWidth || 1))
577 } else {
578 let params = {}
579 if (r_property.fontColor.type == 'texture') {
580 params.tx = anchor[0]
581 params.ty = anchor[1] + marginTop
582 }
583 let fillStyle = toPaint(r_property.fontColor, anchor, params)
584 rNode.attr('fill', fillStyle)
585 }
586 addRunTextEvent(rNode, obj)
587 return rNode
588 }
589 maxFontSize = Math.max(fontSize, maxFontSize)
590 let rNode = createRNode(pNode)
591 if (isVertical) {
592 // 竖版
593 rNode.text(r.text)
594 } else {
595 // 横版
596 rNode.text(r.text)
597 if (wordWrap && pNode.node().getComputedTextLength() > maxWidth && r.text.length > 1) {
598 // 自动换行
599 let start = 0
600 for (let s = 1; s <= r.text.length; s++) {
601 let text = r.text.substring(start, s)
602 rNode.text(text)
603 if (pNode.node().getComputedTextLength() > maxWidth - maxFontSize / 2) {
604 marginTop += (maxFontSize * lineSpacing)
605 pNode.attr('y', anchor[1] + marginTop)
606 pNode = createPNode()
607 rNode = createRNode(pNode)
608 start = s
609 }
610 }
611 }
612 }
613 }
614 if (isVertical) {
615 if (verticalAlignment == 'MIDDLE') {
616 pNode.attr('x', anchor[0] + maxFontSize * (obj.children.length - i - 1) + (anchor[2] - maxFontSize * obj.children.length) / 2)
617 } else if (verticalAlignment == 'TOP') {
618 pNode.attr('x', anchor[0] + anchor[2] - maxFontSize * (i + 1))
619 } else {
620 pNode.attr('x', anchor[0] + maxFontSize * (obj.children.length - i - 1))
621 }
622 pNode.attr('y', anchor[1])
623 } else {
624 marginTop += (maxFontSize * lineSpacing)
625 pNode.attr('y', anchor[1] + marginTop)
626 }
627 }
628 let yHeight = 0
629 if (verticalAlignment == 'MIDDLE') {
630 let height = textNode.node().getBBox().height
631 yHeight = (anchor[3] - height - textInsets[0] - textInsets[2]) / 2 + textInsets[0] - maxFontSize / 4
632 } else if (verticalAlignment == 'BOTTOM') {
633 let height = textNode.node().getBBox().height
634 yHeight = anchor[3] - height - textInsets[2]
635 }
636 if (yHeight != 0) {
637 let childNodes = textNode.node().childNodes
638 for (let i = 0; i < childNodes.length; i++) {
639 let pNode = childNodes[i]
640 let y = +pNode.getAttribute('y') + yHeight
641 pNode.setAttribute('y', y)
642 }
643 }
644 }
645
646 function drawImage(obj, parent) {
647 let property = obj.extInfo.property
648 let g = shapeHandle(property, parent)
649 g.attr('id', 'g-' + obj.id)
650 addElementEvent(g, obj)
651 if (property.fillStyle && property.fillStyle.texture) {
652 property.fillStyle.texture.imageData = property.image
653 // 图片自带拉伸
654 property.fillStyle.texture.stretch = property.fillStyle.texture.stretch || [0, 0, 0, 0]
655 } else {
656 property.fillStyle = { 'type': 'texture', texture: { 'imageData': property.image, insets: property.clipping, stretch: [0, 0, 0, 0] } }
657 }
658 drawGeometry(property, g, obj.id)
659 }
660
661 function drawTableColumn(property, parent) {
662 let anchor = scaleAnchor(property.anchor)
663 let x = anchor[0] - 1
664 let endX = anchor[0] + anchor[2] + 1
665 let y = anchor[1] - 1
666 let endY = anchor[1] + anchor[3] + 1
667 let borders = scaleAnchor(property.borders) // top/left/bottom/right
668 // top
669 let top = borders[0]
670 let lineWidth = scaleValue(top.lineWidth)
671 let stroke = toColor({ color: top.color }, 'white')
672 parent.append('line')
673 .attr('x1', x).attr('y1', y)
674 .attr('x2', endX).attr('y2', y)
675 .attr('style', `stroke: ${stroke};stroke-width: ${lineWidth}`)
676 // right
677 let right = borders[3]
678 lineWidth = scaleValue(right.lineWidth)
679 stroke = toColor({ color: right.color }, 'white')
680 parent.append('line')
681 .attr('x1',endX).attr('y1', y)
682 .attr('x2', endX).attr('y2', endY)
683 .attr('style', `stroke: ${stroke};stroke-width: ${lineWidth}`)
684 // bottom
685 let bottom = borders[2]
686 lineWidth = scaleValue(bottom.lineWidth)
687 stroke = toColor({ color: bottom.color }, 'white')
688 parent.append('line')
689 .attr('x1',endX).attr('y1', endY)
690 .attr('x2', x).attr('y2', endY)
691 .attr('style', `stroke: ${stroke};stroke-width: ${lineWidth}`)
692 // left
693 let left = borders[1]
694 lineWidth = scaleValue(left.lineWidth)
695 stroke = toColor({ color: left.color }, 'white')
696 parent.append('line')
697 .attr('x1',x).attr('y1', endY)
698 .attr('x2', x).attr('y2', y)
699 .attr('style', `stroke: ${stroke};stroke-width: ${lineWidth}`)
700 if (property.fillStyle) {
701 let fill = toPaint(property.fillStyle, anchor)
702 parent.append('rect')
703 .attr('x', anchor[0]).attr('y', anchor[1])
704 .attr('width', anchor[2])
705 .attr('height', anchor[3])
706 .attr('fill', fill)
707 .attr('stroke', 'none')
708 }
709 }
710
711 function drawDiagram(obj, parent) {
712 let property = obj.extInfo.property
713 let anchor = scaleAnchor(property.anchor)
714 let g = parent.append('g').attr('id', obj.id).attr('id', 'g-' + obj.id)
715 addElementEvent(g, obj)
716 g.attr('transform', `translate(${anchor[0]}, ${anchor[1]})`)
717 for (let i = 0; i < obj.children.length; i++) {
718 drawElement(obj.children[i], g)
719 }
720 }
721
722 function drawContainer(obj, parent) {
723 let property = obj.extInfo.property
724 let _groupFlipX = ctx.groupFlipX
725 let _groupFlipY = ctx.groupFlipY
726 let _groupRotation = ctx.groupRotation
727 let g = shapeHandle(property, parent)
728 g.attr('id', obj.id).attr('id', 'g-' + obj.id)
729 addElementEvent(g, obj)
730 if (property.realType == 'Group') {
731 let parentGroupFillStyle = ctx.groupFillStyle
732 let groupFillStyle = property.groupFillStyle
733 if (groupFillStyle) {
734 groupFillStyle = JSON.parse(JSON.stringify(groupFillStyle))
735 groupFillStyle.groupAnchor = scaleAnchor(property.anchor)
736 if (parentGroupFillStyle) {
737 groupFillStyle.parentGroupFillStyle = parentGroupFillStyle
738 }
739 }
740 ctx.groupFillStyle = groupFillStyle
741 let copyChildren = JSON.parse(JSON.stringify(obj.children))
742 recursionGroupChildren(copyChildren, c => {
743 if (c.extInfo && c.extInfo.property && c.extInfo.property.anchor) {
744 let anchor = c.extInfo.property.anchor
745 anchor[0] *= ctx.interior.scaleX
746 anchor[1] *= ctx.interior.scaleY
747 anchor[2] *= ctx.interior.scaleX
748 anchor[3] *= ctx.interior.scaleY
749 }
750 })
751 for (let i = 0; i < copyChildren.length; i++) {
752 drawElement(copyChildren[i], g)
753 }
754 ctx.interior = ctx._interior
755 } else {
756 for (let i = 0; i < obj.children.length; i++) {
757 drawElement(obj.children[i], g)
758 }
759 }
760 ctx.groupFlipX = _groupFlipX
761 ctx.groupFlipY = _groupFlipY
762 ctx.groupRotation = _groupRotation
763 }
764
765 function recursionGroupChildren(children, fn) {
766 if (!children) {
767 return
768 }
769 for (let i = 0; i < children.length; i++) {
770 let c = children[i]
771 fn(c)
772 if (c.extInfo && c.extInfo.property.realType == 'Group') {
773 continue
774 }
775 if (c.children && c.children.length > 0) {
776 recursionGroupChildren(c.children, fn)
777 }
778 }
779 }
780
781 function drawConnector(obj, parent) {
782 let property = obj.extInfo.property
783 let g = parent.append('g').attr('id', 'g-' + obj.id)
784 addElementEvent(g, obj)
785 drawGeometry(property, g, obj.id)
786 }
787
788 function drawGraphicFrame(obj, parent) {
789 let property = obj.extInfo.property
790 let anchor = scaleAnchor(property.anchor)
791 let g = parent.append('g').attr('id', 'g-' + obj.id)
792 addElementEvent(g, obj)
793 if (property.chart && property.chart.chartData && property.chart.chartData.length > 0) {
794 drawChart(property.chart, [0, 0, anchor[2], anchor[3]]).then(canvas => {
795 let imgSrc = canvas.toDataURL('image/png')
796 let fill = toPaint({ 'type': 'texture', texture: { 'imageData': imgSrc, stretch: [0, 0, 0, 0] } }, anchor)
797 g.append('rect')
798 .attr('id', obj.id)
799 .attr('width', anchor[2])
800 .attr('height', anchor[3])
801 .attr('transform', `translate(${anchor[0]}, ${anchor[1]})`)
802 .attr('fill', fill)
803 })
804 }
805 }
806
807 function drawTable(obj, parent) {
808 let property = obj.extInfo.property
809 let g = parent.append('g').attr('id', obj.id).attr('id', 'g-' + obj.id)
810 addElementEvent(g, obj)
811 for (let i = 0; i < obj.children.length; i++) {
812 let row = obj.children[i]
813 drawTableRow(obj, row, g)
814 }
815 }
816
817 function drawTableRow(table, row, parent) {
818 let property = row.extInfo.property
819 for (let i = 0; i < row.children.length; i++) {
820 let column = row.children[i]
821 drawText(column, parent)
822 }
823 }
824
825 function drawGeometry(property, parent, id) {
826 if (!parent) {
827 parent = svg
828 }
829 let anchor = scaleAnchor(property.anchor)
830 let stroke = null
831 let lineWidth = scaleValue(1)
832 if (property.strokeStyle) {
833 lineWidth = scaleValue(property.strokeStyle.lineWidth || 1)
834 stroke = toPaint(property.strokeStyle.paint, anchor)
835 }
836 let paths = geometryPaths(property, zoom)
837 for (let i = 0; i < paths.length; i++) {
838 let path = paths[i]
839 let pathNode = parent.append('path')
840 if (id) {
841 pathNode.attr('id', id)
842 }
843 pathNode.attr('d', path.path)
844 let transform = `translate(${anchor[0]},${anchor[1]})`
845 if (path.scaleX && path.scaleY) {
846 transform += ` scale(${path.scaleX || 1},${path.scaleY || 1})`
847 }
848 pathNode.attr('transform', transform)
849 let fill = null
850 if (property.fillStyle) {
851 let scaleX = 1 / path.scaleX
852 let scaleY = 1 / path.scaleY
853 let params = { scaleX, scaleY }
854 if (property.fillStyle.type == 'bgFill') {
855 if (ctx.interior) {
856 params.tx = -ctx.interior.tx - anchor[0]
857 params.ty = -ctx.interior.ty - anchor[1]
858 } else {
859 params.tx = -anchor[0]
860 params.ty = -anchor[1]
861 }
862 params.width = anchor[2]
863 params.height = anchor[3]
864 params.rotation = -((property.rotation || 0) + (ctx.groupRotation || 0))
865 }
866 fill = toPaint(property.fillStyle, anchor, params)
867 }
868 if (path.filled && fill) {
869 pathNode.attr('fill', fill)
870 } else {
871 pathNode.attr('fill', 'transparent')
872 }
873 if (path.stroked) {
874 pathNode.attr('stroke', stroke)
875 pathNode.attr('stroke-width', path.scaleY ? lineWidth / path.scaleY : lineWidth)
876 }
877 let filter = toShadow(property.shadow, anchor, path.scaleX, path.scaleY)
878 if (filter) {
879 pathNode.attr('filter', filter)
880 }
881 }
882 }
883
884 function toShadow(shadow, anchor, scaleX, scaleY) {
885 if (!shadow) {
886 return null
887 }
888 scaleX = scaleX || 1
889 scaleY = scaleY || 1
890 let stdDeviation = (shadow.blur || 0) / Math.max(scaleX, scaleY) / 5
891 let distance = scaleValue(shadow.distance)
892 let color = toColor(shadow.fillStyle.color)
893 let filterId = 'shadow' + (++counter)
894 let filter = defs.append('filter').attr('id', filterId)
895 if (anchor && distance && (distance >= anchor[2] / 2 || distance >= anchor[3] / 2)) {
896 filter.attr('x', '-100%').attr('y', '-100%').attr('width', '400%').attr('height', '400%')
897 } else {
898 filter.attr('x', '-50%').attr('y', '-50%').attr('width', '200%').attr('height', '200%')
899 }
900 filter.append('feGaussianBlur').attr('in', 'SourceAlpha').attr('stdDeviation', stdDeviation).attr('result', 'blur')
901 filter.append('feFlood').attr('flood-color', color).attr('result', 'color')
902 filter.append('feComposite').attr('in', 'color').attr('in2', 'blur').attr('operator', 'in').attr('result', 'shadow')
903 if (distance) {
904 let radians = (shadow.angle || 0) * Math.PI / 180
905 let x = 0, y = 0
906 let rx = distance / scaleX
907 let ry = distance / scaleY
908 let shadowOffsetX = x + rx * Math.cos(radians)
909 let shadowOffsetY = y + ry * Math.sin(radians)
910 filter.append('feOffset').attr('dx', shadowOffsetX).attr('dy', shadowOffsetY).attr('result', 'offsetBlur')
911 }
912 let feMerge = filter.append('feMerge')
913 feMerge.append('feMergeNode').attr('in', 'offsetBlur')
914 feMerge.append('feMergeNode').attr('in', 'SourceGraphic')
915 return `url(#${filterId})`
916 }
917
918 function toPaint(paint, anchor, params) {
919 if (!paint) {
920 return 'transparent'
921 } else if (paint.type == 'noFill') {
922 // 无填充
923 return 'transparent'
924 } else if (paint.type == 'color') {
925 // 颜色
926 return toColor(paint.color)
927 } else if (paint.type == 'bgFill') {
928 // 背景填充
929 if (ctx.bgFillStyle) {
930 return toPaint(ctx.bgFillStyle, ctx.bgAnchor || anchor, params)
931 } else {
932 return 'transparent'
933 }
934 } else if (paint.type == 'groupFill') {
935 // 组合背景
936 let groupFillStyle = paint.parentGroupFillStyle || ctx.groupFillStyle
937 if (groupFillStyle) {
938 return toPaint(groupFillStyle, scaleAnchor(groupFillStyle.groupAnchor) || anchor, params)
939 } else {
940 return 'transparent'
941 }
942 } else if (paint.type == 'gradient') {
943 // 渐变
944 let gradient = paint.gradient
945 let x = 0, y = 0
946 let width = anchor[2], height = anchor[3]
947 let centerX = x + width / 2
948 let centerY = y + height / 2
949 let gradientNode
950 // linear,circular,rectangular,shape
951 if (gradient.gradientType == 'circular') {
952 // 射线
953 let cx = centerX + width * (gradient.insets[1] - gradient.insets[3]) / 2
954 let cy = centerY + height * (gradient.insets[0] - gradient.insets[2]) / 2
955 // let radius = Math.sqrt(width * width + height * height) * (gradient.insets[1] == 0.5 ? 0.5 : 1)
956 let _cx = ((cx / width) * 100).toFixed(0) + '%'
957 let _cy = ((cy / height) * 100).toFixed(0) + '%'
958 let _r = (gradient.insets[1] * 100).toFixed(0) + '%'
959 gradientNode = defs.append('radialGradient').attr('cx', _cx).attr('cy', _cy).attr('r', _r).attr('fx', _cx).attr('fy', _cy)
960 } else {
961 // 线性
962 let startX = x
963 let startY = centerY
964 let endX = x + width
965 let endY = centerY
966 if (gradient.angle) {
967 let radians = gradient.angle * Math.PI / 180
968 let midX = (startX + endX) / 2
969 let midY = (startY + endY) / 2
970 let newStartX = midX + (startX - midX) * Math.cos(radians) - (startY - midY) * Math.sin(radians)
971 let newStartY = midY + (startX - midX) * Math.sin(radians) + (startY - midY) * Math.cos(radians)
972 let newEndX = midX + (endX - midX) * Math.cos(radians) - (endY - midY) * Math.sin(radians)
973 let newEndY = midY + (endX - midX) * Math.sin(radians) + (endY - midY) * Math.cos(radians)
974 startX = newStartX
975 startY = newStartY
976 endX = newEndX
977 endY = newEndY
978 }
979 let x1 = ((startX / width) * 100).toFixed(0) + '%'
980 let y1 = ((startY / height) * 100).toFixed(0) + '%'
981 let x2 = ((endX / width) * 100).toFixed(0) + '%'
982 let y2 = ((endY / height) * 100).toFixed(0) + '%'
983 gradientNode = defs.append('linearGradient').attr('x1', x1).attr('y1', y1).attr('x2', x2).attr('y2', y2)
984 }
985 let gradientId = 'gradient' + (++counter)
986 gradientNode.attr('id', gradientId)
987 for (let i = 0; i < gradient.colors.length; i++) {
988 let color = toColor(gradient.colors[i])
989 let offset = (gradient.fractions[i] * 100).toFixed(0) + '%'
990 gradientNode.append('stop').attr('offset', offset).attr('stop-color', color)
991 }
992 return `url(#${gradientId})`
993 } else if (anchor && anchor[2] == 0 && anchor[3] == 0) {
994 return 'transparent'
995 } else if (paint.type == 'texture') {
996 // 图片或纹理
997 let texture = paint.texture
998 let patternId = 'pattern' + (++counter)
999 let patternNode = defs.append('pattern').attr('id', patternId).attr('patternUnits', 'userSpaceOnUse')
1000 if (params) {
1001 let transform = []
1002 if (params.scaleX && params.scaleY) {
1003 transform.push(`scale(${params.scaleX}, ${params.scaleY})`)
1004 }
1005 if (params.rotation) {
1006 let width = params.width || anchor[2]
1007 let height = params.height || anchor[3]
1008 transform.push(`rotate(${params.rotation}, ${width / 2}, ${height / 2})`)
1009 }
1010 if (params.tx != null && params.ty != null) {
1011 transform.push(`translate(${params.tx}, ${params.ty})`)
1012 }
1013 if (transform.length > 0) {
1014 patternNode.attr('patternTransform', transform.join(' '))
1015 }
1016 }
1017 loadImage(texture.imageData).then(img => {
1018 if (img) {
1019 texturePattern(patternNode, img, texture, anchor)
1020 }
1021 })
1022 return `url(#${patternId})`
1023 } else if (paint.type == 'pattern') {
1024 // 图案
1025 let pattern = paint.pattern
1026 // let prst = pattern.prst
1027 let fgColor = pattern.fgColor.realColor
1028 let bgColor = pattern.bgColor.realColor
1029 let width = anchor[2], height = anchor[3]
1030 let imgCanvas = document.createElement('canvas')
1031 imgCanvas.width = width
1032 imgCanvas.height = height
1033 let imgCtx = imgCanvas.getContext('2d')
1034 imgCtx.imageSmoothingEnabled = true
1035 imgCtx.imageSmoothingQuality = 'high'
1036 let imgData = imgCtx.createImageData(width, height)
1037 let line = 0
1038 for (let i = 0; i < imgData.data.length; i += 4) {
1039 if (++line % 16 == 0) {
1040 // 前景
1041 imgData.data[i + 0] = (fgColor >> 16) & 255
1042 imgData.data[i + 1] = (fgColor >> 8) & 255
1043 imgData.data[i + 2] = (fgColor >> 0) & 255
1044 imgData.data[i + 3] = (fgColor >> 24) & 255
1045 } else {
1046 // 背景
1047 imgData.data[i + 0] = (bgColor >> 16) & 255
1048 imgData.data[i + 1] = (bgColor >> 8) & 255
1049 imgData.data[i + 2] = (bgColor >> 0) & 255
1050 imgData.data[i + 3] = (bgColor >> 24) & 255
1051 }
1052 if (i % 400 == 0) {
1053 line += 2
1054 }
1055 }
1056 imgCtx.putImageData(imgData, 0, 0)
1057 let imgSrc = imgCanvas.toDataURL('image/png')
1058 let patternId = 'pattern' + (++counter)
1059 let patternNode = defs.append('pattern')
1060 patternNode.attr('id', patternId)
1061 .attr('width', width)
1062 .attr('height', height)
1063 .attr('patternUnits', 'userSpaceOnUse')
1064 patternNode.append('image')
1065 .attr('href', imgSrc)
1066 .attr('x', 0)
1067 .attr('y', 0)
1068 .attr('width', width)
1069 .attr('height', height)
1070 if (params) {
1071 let transform = []
1072 if (params.scaleX && params.scaleY) {
1073 transform.push(`scale(${params.scaleX},${params.scaleY})`)
1074 }
1075 if (transform.length > 0) {
1076 patternNode.attr('patternTransform', transform.join(' '))
1077 }
1078 }
1079 return `url(#${patternId})`
1080 }
1081 }
1082
1083 function texturePattern(patternNode, img, texture, anchor) {
1084 let width = anchor[2]
1085 let height = anchor[3]
1086 if (width < 1 && height < 1 || isNaN(width) || isNaN(height)) {
1087 width = img.width
1088 height = img.height
1089 }
1090 if (texture.alignment || !texture.stretch) {
1091 // 图片平铺
1092 width = img.width
1093 height = img.height
1094 }
1095 patternNode.attr('width', width).attr('height', height)
1096 let patternCanvas = document.createElement('canvas')
1097 patternCanvas.width = Math.max(1, width)
1098 patternCanvas.height = Math.max(1, height)
1099 let patternCtx = patternCanvas.getContext('2d')
1100 patternCtx.imageSmoothingEnabled = true
1101 patternCtx.imageSmoothingQuality = 'high'
1102 if (texture.alpha >= 0 && texture.alpha < 100000) {
1103 patternCtx.globalAlpha = texture.alpha / 100000
1104 }
1105 let imgInsets = null
1106 if (texture.insets) {
1107 let top = texture.insets[0] / 100000
1108 let left = texture.insets[1] / 100000
1109 let bottom = texture.insets[2] / 100000
1110 let right = texture.insets[3] / 100000
1111 let x = img.width * left
1112 let y = img.height * top
1113 let w = img.width * (1 - left - right)
1114 let h = img.height * (1 - top - bottom)
1115 imgInsets = [x, y, w, h]
1116 }
1117 if (texture.stretch) {
1118 let top = texture.stretch[0] / 100000
1119 let left = texture.stretch[1] / 100000
1120 let bottom = texture.stretch[2] / 100000
1121 let right = texture.stretch[3] / 100000
1122 let x = width * left
1123 let y = height * top
1124 let w = width * (1 - left - right)
1125 let h = height * (1 - top - bottom)
1126 if (imgInsets) {
1127 patternCtx.drawImage(img, imgInsets[0], imgInsets[1], imgInsets[2], imgInsets[3], x, y, w, h)
1128 } else {
1129 patternCtx.drawImage(img, x, y, w, h)
1130 }
1131 } else if (texture.alignment) {
1132 let x = 0, y = 0, scale = zoom
1133 if (texture.alignment == 'CENTER') {
1134 if (width > anchor[2] / scale) {
1135 x = (width - anchor[2] / scale) / 2
1136 }
1137 if (height > anchor[3] / scale) {
1138 y = (height - anchor[3] / scale) / 2
1139 }
1140 }
1141 if (!x && !y) {
1142 scale = Math.max(scale, 1)
1143 }
1144 patternCtx.drawImage(img, x, y, width, height, 0, 0, width * scale, height * scale)
1145 } else {
1146 if (imgInsets) {
1147 patternCtx.drawImage(img, imgInsets[0], imgInsets[1], imgInsets[2], imgInsets[3], 0, 0, width, height)
1148 } else {
1149 patternCtx.drawImage(img, 0, 0, width, height)
1150 }
1151 }
1152 if (texture.duoTone && texture.duoTone.length > 0 && texture.duoTonePrst) {
1153 try {
1154 // 重新着色
1155 let color = texture.duoTone[0].realColor
1156 let r = (color >> 16) & 255
1157 let g = (color >> 8) & 255
1158 let b = (color >> 0) & 255
1159 let imageData = patternCtx.getImageData(0, 0, patternCanvas.width, patternCanvas.height)
1160 let data = imageData.data
1161 for(var i = 0; i < data.length; i += 4) {
1162 let gray = (data[i] * 0.3 + data[i + 1] * 0.59 + data[i + 2] * 0.11) / 255
1163 // black / white
1164 let prst = texture.duoTonePrst == 'white' ? 255 : 0
1165 data[i] = gray * r + (1 - gray) * prst
1166 data[i + 1] = gray * g + (1 - gray) * prst
1167 data[i + 2] = gray * b + (1 - gray) * prst
1168 }
1169 patternCtx.putImageData(imageData, 0, 0)
1170 } catch(e) { /* empty */ }
1171 }
1172 // let mode = texture.alignment ? 'repeat' : 'no-repeat'
1173 let imgSrc = patternCanvas.toDataURL('image/png')
1174 patternNode.append('image')
1175 .attr('href', imgSrc)
1176 .attr('x', 0)
1177 .attr('y', 0)
1178 .attr('width', width)
1179 .attr('height', height)
1180 }
1181
1182 function toColor(colorObj, defaultColor) {
1183 if (colorObj == null || (colorObj.color == null && colorObj.realColor == null)) {
1184 return defaultColor || 'transparent'
1185 }
1186 let color = colorObj.realColor != null ? colorObj.realColor : colorObj.color
1187 let r = (color >> 16) & 255
1188 let g = (color >> 8) & 255
1189 let b = (color >> 0) & 255
1190 let a = ((color >> 24) & 255) / 255
1191 if (colorObj.realColor == null) {
1192 if (colorObj.alpha != null && colorObj.alpha != -1) {
1193 if (colorObj.alpha > 1000) {
1194 a = colorObj.alpha / 100000
1195 } else {
1196 a = (colorObj.alpha > 0 && colorObj.alpha < 1) ? colorObj.alpha : colorObj.alpha / 255
1197 }
1198 a = Math.min(1, Math.max(0, a))
1199 }
1200 if (colorObj.lumMod && colorObj.lumMod > 0) {
1201 let value = colorObj.lumMod / 100000
1202 r = r * value
1203 g = g * value
1204 b = b * value
1205 }
1206 if (colorObj.lumOff && colorObj.lumOff > 0) {
1207 let value = colorObj.lumOff / 100000
1208 r += 255 * value
1209 g += 255 * value
1210 b += 255 * value
1211 }
1212 }
1213 return `rgba(${r}, ${g}, ${b}, ${a})`
1214 }
1215
1216 function toColorValue(r, g, b, a) {
1217 a = (a == null ? 255 : a)
1218 return (a & 255) << 24 | (r & 255) << 16 | (g & 255) << 8 | (b & 255) << 0
1219 }
1220
1221 function text(obj) {
1222 if (obj == null) {
1223 return null
1224 }
1225 let text = obj.text
1226 if (text != null) {
1227 return text
1228 }
1229 if (obj.children != null && obj.children.length > 0) {
1230 let textAll = ''
1231 for (let i = 0; i < obj.children.length; i++) {
1232 let p = obj.children[i]
1233 if (p.children == null) {
1234 continue
1235 }
1236 let pText = ''
1237 for (let j = 0; j < p.children.length; j++) {
1238 let r = p.children[j]
1239 if (r.text == null) {
1240 continue
1241 }
1242 pText += r.text
1243 }
1244 if (pText) {
1245 textAll += (pText + '\n')
1246 }
1247 }
1248 if (textAll.length > 0) {
1249 textAll = textAll.substring(0, textAll.length - 1)
1250 }
1251 return textAll
1252 }
1253 return ''
1254 }
1255
1256 function loadImage(src) {
1257 return new Promise(resolve => {
1258 if (!src) {
1259 resolve()
1260 return
1261 }
1262 let cacheKey
1263 if (src.length < 15) {
1264 cacheKey = src
1265 } else {
1266 cacheKey = src.length + '_' + src.substring(src.length - 15)
1267 }
1268 let img = imageCache[cacheKey]
1269 if (img == null) {
1270 img = new Image()
1271 let eqOrigin = src.startsWith('data:') || src.startsWith(document.location.origin) || (src.startsWith('//') && (document.location.protocol + src).startsWith(document.location.origin))
1272 if (!eqOrigin) {
1273 img.crossOrigin = 'anonymous'
1274 }
1275 img.src = src
1276 img.onload = function() {
1277 imageCache[cacheKey] = img
1278 resolve(img)
1279 }
1280 img.onerror = function (e) {
1281 resolve()
1282 console.log('图片加载失败: ', src, e)
1283 }
1284 } else {
1285 resolve(img)
1286 }
1287 })
1288 }
1289
1290 function calcPointList(page) {
1291 pointList = []
1292 recursion(page.children, c => {
1293 if (c.extInfo && c.extInfo.property && c.extInfo.property.anchor) {
1294 let point = scaleAnchor(c.point)
1295 if (!point) {
1296 point = scaleAnchor(c.extInfo.property.anchor)
1297 }
1298 let _point
1299 if (point[2] < 0.1) {
1300 let width = scaleValue((c.extInfo.property.strokeStyle || {}).lineWidth || 1)
1301 let x = point[0] - width / 2
1302 _point = { x: x, endX: x + width, y: point[1], endY: point[1] + point[3], sort: width + point[3], obj: c }
1303 } else if (point[3] < 0.1) {
1304 let height = scaleValue((c.extInfo.property.strokeStyle || {}).lineWidth || 1)
1305 let y = point[1] - height / 2
1306 _point = { x: point[0], endX: point[0] + point[2], y: y, endY: y + height, sort: point[2] + height, obj: c }
1307 } else {
1308 _point = { x: point[0], endX: point[0] + point[2], y: point[1], endY: point[1] + point[3], sort: point[2] + point[3], obj: c }
1309 }
1310 pointList.push(_point)
1311 }
1312 })
1313 pointList.sort((p1, p2) => p1.sort > p2.sort ? 1 : -1)
1314 }
1315
1316 function removePoint() {
1317 if (currentPoint && currentPoint.obj && (!currentSelect || currentPoint.obj.id != currentSelect.id)) {
1318 let gNode = document.getElementById('g-' + currentPoint.obj.id)
1319 if (gNode) {
1320 gNode.style.cursor = null
1321 gNode.style.outline = null
1322 }
1323 }
1324 currentPoint = null
1325 }
1326
1327 function showPoint(point) {
1328 removePoint()
1329 currentPoint = point
1330 let gNode = document.getElementById('g-' + currentPoint.obj.id)
1331 if (gNode && (!currentSelect || currentPoint.obj.id != currentSelect.id)) {
1332 gNode.style.cursor = 'move'
1333 gNode.style.outline = '1px dashed #777'
1334 }
1335 }
1336
1337 function addElementEvent(gNode, elementObj) {
1338 let node = gNode.node()
1339 node.addEventListener('click', function (e) {
1340 if (currentPoint && currentSelect && currentPoint.obj.id == currentSelect.id) {
1341 if (!document.getElementById('move_element_operate')) {
1342 addElementMoveScale(elementObj)
1343 }
1344 return
1345 }
1346 if (currentPoint && currentPoint.obj.id != elementObj.id && (currentPoint.obj.type == 'text' || currentPoint.obj.type == 'freeform')) {
1347 // 文本被遮挡处理
1348 let rId = null
1349 for (let i = 0; i < currentPoint.obj.children.length; i++) {
1350 let p = currentPoint.obj.children[i]
1351 for (let j = 0; j < p.children.length; j++) {
1352 let r = p.children[j]
1353 let rNode = document.getElementById(r.id)
1354 let rect = rNode.getBoundingClientRect()
1355 if (e.clientX >= rect.x && e.clientX <= rect.x + rect.width && e.clientY >= rect.y && e.clientY <= rect.y + rect.height) {
1356 rId = r.id
1357 if (r.text && r.text.trim()) {
1358 break
1359 }
1360 }
1361 }
1362 if (rId) {
1363 break
1364 }
1365 }
1366 if (rId) {
1367 editRunText(rId, currentPoint.obj)
1368 e.cancelBubble = true
1369 return
1370 }
1371 }
1372 if (currentPoint && currentPoint.obj.id != elementObj.id) {
1373 addElementMoveScale(elementObj)
1374 } else if (!currentPoint) {
1375 removeElementMoveScale()
1376 }
1377 })
1378 node.addEventListener('mousedown', function (e) {
1379 if (!currentPoint) {
1380 return
1381 }
1382 if (!currentSelect || currentPoint.obj.id != currentSelect.id) {
1383 addElementMoveScale(currentPoint.obj)
1384 }
1385 doElementMove(e, currentPoint.obj)
1386 })
1387 }
1388
1389 function addRunTextEvent(rNode, textObj) {
1390 let rId = rNode.attr('id')
1391 let node = rNode.node()
1392 // node.addEventListener('mouseenter', function () {
1393 // node.style.outline = '1px dashed #f35858'
1394 // })
1395 // node.addEventListener('mouseleave', function () {
1396 // node.style.outline = null
1397 // })
1398 node.addEventListener('click', function (e) {
1399 editRunText(rId, textObj)
1400 e.cancelBubble = true
1401 })
1402 }
1403
1404 function doElementMove(e, obj) {
1405 // 移动
1406 if (obj.type == 'tableColumn') {
1407 // table
1408 obj = idMap[idMap[obj.pid].pid]
1409 }
1410 if (!obj.point) {
1411 return
1412 }
1413 let gNode = document.getElementById('g-' + obj.id)
1414 if (!gNode) {
1415 return
1416 }
1417 let x = e.clientX
1418 let y = e.clientY
1419 let g = d3.select(gNode)
1420 let flipX = parseInt(g.attr('groupFlipX') || 1)
1421 let flipY = parseInt(g.attr('groupFlipY') || 1)
1422 let lastTranslate = g.translate()
1423 let translateX = lastTranslate.x
1424 let translateY = lastTranslate.y
1425 let updateAnchor = (obj, tx, ty) => {
1426 let point = obj.point
1427 point[0] = point[0] + tx * flipX
1428 point[1] = point[1] + ty * flipY
1429 let anchor = obj.extInfo.property.anchor
1430 if (point !== anchor) {
1431 anchor[0] = anchor[0] + tx * (anchor[2] / point[2])
1432 anchor[1] = anchor[1] + ty * (anchor[3] / point[3])
1433 }
1434 let interiorAnchor = obj.extInfo.property.interiorAnchor
1435 if (interiorAnchor) {
1436 interiorAnchor[0] = interiorAnchor[0] + tx * (interiorAnchor[2] / point[2])
1437 interiorAnchor[1] = interiorAnchor[1] + ty * (interiorAnchor[3] / point[3])
1438 }
1439 if (obj.type == 'table') {
1440 let rows = obj.children || []
1441 for (let i = 0; i < rows.length; i++) {
1442 let columns = rows[i].children || []
1443 for (let j = 0; j < columns.length; j++) {
1444 let columnAnchor = columns[j].extInfo.property.anchor
1445 columnAnchor[0] = columnAnchor[0] + tx
1446 columnAnchor[1] = columnAnchor[1] + ty
1447 }
1448 }
1449 }
1450 }
1451 let rotate = g.rotate()
1452 let move_element_operate = document.getElementById('move_element_operate')
1453 let tx = 0, ty = 0
1454 let lastMoveX = +(g.attr('moveX') || 0)
1455 let lastMoveY = +(g.attr('moveY') || 0)
1456 document.onmousemove = function (e2) {
1457 tx = (e2.clientX - x) * flipX
1458 ty = (e2.clientY - y) * flipY
1459 if (move_element_operate) {
1460 move_element_operate.remove()
1461 move_element_operate = null
1462 currentSelect = null
1463 }
1464 if (rotate) {
1465 g.rotate(0)
1466 }
1467 g.translate(translateX + tx, translateY + ty)
1468 }
1469 let cursor = g.style('cursor')
1470 g.style('cursor', 'move')
1471 document.onmouseup = function (e2) {
1472 g.style('cursor', cursor)
1473 document.onmousemove = null
1474 document.onmouseup = null
1475 if (rotate) {
1476 g.rotate(rotate.rotate, rotate.x, rotate.y)
1477 }
1478 if (tx || ty) {
1479 const real_tx = tx / zoom
1480 const real_ty = ty / zoom
1481 updateAnchor(obj, real_tx, real_ty)
1482 if (obj != idMap[obj.id]) {
1483 updateAnchor(idMap[obj.id], real_tx, real_ty)
1484 }
1485 g.attr('moveX', lastMoveX + tx)
1486 g.attr('moveY', lastMoveY + ty)
1487 // 重新渲染元素
1488 // $this.redrawElementWithId(obj.id)
1489 // 不重新渲染,重新计算元素位置
1490 page && calcPointList(page)
1491 if ($this.onchange) {
1492 $this.onchange(page, 'move', obj, e2)
1493 }
1494 }
1495 setTimeout(() => addElementMoveScale(obj), 10)
1496 }
1497 }
1498
1499 function doElementRotate(e, obj) {
1500 // 旋转
1501 if (obj.type == 'tableColumn') {
1502 // table
1503 obj = idMap[idMap[obj.pid].pid]
1504 }
1505 if (!obj.point) {
1506 return
1507 }
1508 let gNode = document.getElementById('g-' + obj.id)
1509 if (!gNode) {
1510 return
1511 }
1512 let g = d3.select(gNode)
1513 let flipX = parseInt(g.attr('groupFlipX') || 1)
1514 let flipY = parseInt(g.attr('groupFlipY') || 1)
1515 let rotate = g.rotate()
1516 if (!rotate.rotate && !rotate.x) {
1517 let anchor = scaleAnchor(obj.extInfo?.property?.anchor ? obj.extInfo.property.anchor : obj.point)
1518 let interior = g.attr('interior')
1519 if (interior) {
1520 interior = JSON.parse(interior)
1521 anchor = [anchor[0] * interior.scaleX + interior.tx * zoom, anchor[1] * interior.scaleY + interior.ty * zoom, anchor[2] * interior.scaleX, anchor[3] * interior.scaleY]
1522 }
1523 let cx = anchor[0] + anchor[2] / 2
1524 let cy = anchor[1] + anchor[3] / 2
1525 rotate = { rotate: 0, x: cx, y: cy, interior: true }
1526 }
1527 let rect = gNode.getBoundingClientRect()
1528 let centerX = rect.x + rect.width / 2
1529 let centerY = rect.y + rect.height / 2
1530 let startAngle = Math.atan2(e.clientY - centerY, e.clientX - centerX) * 180 / Math.PI - rotate.rotate
1531 let updateRotate = (obj, angle) => {
1532 obj.extInfo.property.rotation = angle
1533 }
1534 let angle = 0
1535 let move_rotate_span = document.createElement('span')
1536 move_rotate_span.id = 'move_rotate_span'
1537 move_rotate_span.style.fontSize = '14px'
1538 move_rotate_span.style.color = '#5f6063'
1539 move_rotate_span.style.position = 'fixed'
1540 move_rotate_span.style.left = e.clientX + 'px'
1541 move_rotate_span.style.top = e.clientY + 'px'
1542 move_rotate_span.style.margin = '-20px 0px 0px 12px'
1543 move_rotate_span.innerText = rotate.rotate.toFixed(0) + '°'
1544 document.body.appendChild(move_rotate_span)
1545 let move_element_operate = document.getElementById('move_element_operate')
1546 document.onmousemove = function (e2) {
1547 if (move_element_operate) {
1548 move_element_operate.remove()
1549 move_element_operate = null
1550 currentSelect = null
1551 }
1552 let endAngle = Math.atan2(e2.clientY - centerY, e2.clientX - centerX) * 180 / Math.PI
1553 angle = (endAngle - startAngle) % 360 * flipX
1554 angle = +(angle < 0 ? angle + 360 : angle).toFixed(0)
1555 g.rotate(angle, rotate.x, rotate.y)
1556 move_rotate_span.style.left = e2.clientX + 'px'
1557 move_rotate_span.style.top = e2.clientY + 'px'
1558 move_rotate_span.innerText = angle + '°'
1559 }
1560 document.onmouseup = function (e2) {
1561 document.onmousemove = null
1562 document.onmouseup = null
1563 move_rotate_span.remove()
1564 if (angle) {
1565 updateRotate(obj, angle)
1566 if (obj != idMap[obj.id]) {
1567 updateRotate(idMap[obj.id], angle)
1568 }
1569 if (rotate.interior) {
1570 // 重新渲染元素
1571 $this.redrawElementWithId(obj.id)
1572 } else {
1573 // 不重新渲染,重新计算元素位置
1574 page && calcPointList(page)
1575 setTimeout(() => addElementMoveScale(obj), 10)
1576 }
1577 if ($this.onchange) {
1578 $this.onchange(page, 'rotate', obj, e2)
1579 }
1580 } else {
1581 setTimeout(() => addElementMoveScale(obj), 10)
1582 }
1583 }
1584 }
1585
1586 function rotatePoint(pointX, pointY, centerX, centerY, angle) {
1587 angle = angle * Math.PI / 180
1588 let rotatedX = Math.cos(angle) * (pointX - centerX) - Math.sin(angle) * (pointY - centerY) + centerX
1589 let rotatedY = Math.sin(angle) * (pointX - centerX) + Math.cos(angle) * (pointY - centerY) + centerY
1590 return [rotatedX, rotatedY]
1591 }
1592
1593 function addElementMoveScale(elementObj) {
1594 removeElementMoveScale()
1595 if (!elementObj) {
1596 return
1597 }
1598 if (!elementObj.point || elementObj.type == 'tableColumn') {
1599 return
1600 }
1601 let gNode = document.getElementById('g-' + elementObj.id)
1602 if (!gNode) {
1603 return
1604 }
1605 console.log('obj', elementObj)
1606 let g = d3.select(gNode)
1607 currentSelect = elementObj
1608 // gNode.style.outline = '1px dashed #f35858'
1609 let isText = (elementObj.type == 'text' || elementObj.type == 'freeform') && elementObj.children && elementObj.children.length > 0
1610 if (isText) {
1611 let hasRs = false
1612 for (let i = 0; i < elementObj.children.length; i++) {
1613 if (elementObj.children[i].children && elementObj.children[i].children.length > 0) {
1614 hasRs = true
1615 break
1616 }
1617 }
1618 isText = hasRs
1619 }
1620 let svgNode = svg.node()
1621 let svgRect = svgNode.getClientRects()[0]
1622 let x = svgRect.x + elementObj.point[0] * zoom
1623 let y = svgRect.y + elementObj.point[1] * zoom
1624 let width = elementObj.point[2] * zoom
1625 let height = elementObj.point[3] * zoom
1626 let moveElements = [
1627 { left: '0px', top: '0px', width: '10px', height: '10px', margin: '-5px 0px 0px -5px', cursor: 'nwse-resize' },
1628 { left: width / 2 + 'px', top: '0px', width: '16px', height: '8px', margin: '-4px 0px 0px -8px', cursor: 'ns-resize' },
1629 { left: width + 'px', top: '0px', width: '10px', height: '10px', margin: '-5px 0px 0px -5px', cursor: 'nesw-resize' },
1630 { left: width + 'px', top: height / 2 + 'px', width: '8px', height: '16px', margin: '-8px 0px 0px -4px', cursor: 'ew-resize' },
1631 { left: width + 'px', top: height + 'px', width: '10px', height: '10px', margin: '-5px 0px 0px -5px', cursor: 'nwse-resize' },
1632 { left: width / 2 + 'px', top: height + 'px', width: '16px', height: '8px', margin: '-4px 0px 0px -8px', cursor: 'ns-resize' },
1633 { left: '0px', top: height + 'px', width: '10px', height: '10px', margin: '-5px 0px 0px -5px', cursor: 'nesw-resize' },
1634 { left: '0px', top: height / 2 + 'px', width: '8px', height: '16px', margin: '-8px 0px 0px -4px', cursor: 'ew-resize' }
1635 ]
1636 let div = document.createElement('div')
1637 div.id = 'move_element_operate'
1638 div.style.zIndex = 9999
1639 div.style.position = 'fixed'
1640 div.style.userSelect = 'none'
1641 div.style.pointerEvents = 'none'
1642 div.style.left = x + 'px'
1643 div.style.top = y + 'px'
1644 div.style.width = width + 'px'
1645 div.style.height = height + 'px'
1646 div.style.outline = '1px dashed #f35858'
1647 let rotate = g.rotate().rotate
1648 if (rotate) {
1649 div.style.transform = `rotate(${rotate}deg)`
1650 }
1651 for (let i = 0; i < moveElements.length; i++) {
1652 if (isText && (i == 1 || i == 5)) {
1653 continue
1654 }
1655 let obj = moveElements[i]
1656 let span = document.createElement('span')
1657 span.id = 'move_span_' + i
1658 span.style.pointerEvents = 'all'
1659 span.style.position = 'absolute'
1660 span.style.background = '#fff'
1661 span.style.borderRadius = '5px'
1662 span.style.border = '1px solid #a1a4ad'
1663 span.style.left = obj.left
1664 span.style.top = obj.top
1665 span.style.width = obj.width
1666 span.style.height = obj.height
1667 span.style.cursor = obj.cursor
1668 span.style.margin = obj.margin
1669 span.addEventListener('mousedown', function (e) {
1670 // 缩放
1671 doElementMoveScale(e, elementObj, i)
1672 })
1673 div.appendChild(span)
1674 }
1675 let img = document.createElement('img')
1676 img.id = 'move_rotate_img'
1677 img.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAUCAYAAABvVQZ0AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTM4IDc5LjE1OTgyNCwgMjAxNi8wOS8xNC0wMTowOTowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkNFRjkzREQyQUVBRDExRUE5RDkwQTZCNzUwQUEwQkI5IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkNFRjkzREQzQUVBRDExRUE5RDkwQTZCNzUwQUEwQkI5Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6Q0VGOTNERDBBRUFEMTFFQTlEOTBBNkI3NTBBQTBCQjkiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6Q0VGOTNERDFBRUFEMTFFQTlEOTBBNkI3NTBBQTBCQjkiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz6qKiSpAAACtElEQVR42pRUX2iSURQ/3/4Qpc6EHipKrdwcGOxlDCsI11gPbWt/zCnR2GqNELZRsJ4qG8Sgh+qlxmJREQS1VUTSgiLIoMXoUQVJ9segsIf56cypn06/zrG78WlEeuD3ne/cc+/vnnPuuZcTRREkYkbYEROIF1CEtLQe3/gvk4xfiERWnp/s6WtihHIoUSrWiZYC326d7h+AxaUAhMPhpsrKyl9yuTyFvjBiHjGHmEZ8+RcZx9L0m5qOViMhfA/Mg1KphHQ6DalUClQqFeh0OjAajdDd3Q0NDQ1TrAwf/0oTDQ4XTt6fvAsatRp2aXXA8zxEo9FVFPB4PDA2NgYKhQKsVivYbDYrznfhWnVhZFQzrqPTPFGlrLr26uUzOHTwACSSyc+tbe01w8Pnd3yandVrtdq2kZGL130+H8RiMXA4HMDqmp/msZa2ctQyxLbRUYe5Vq+3LCwsPrl0+YoTx6hmIkN2/M7tIzLZlscGgwHcbjeVYGeX2RKUklF0mxEqRBU7lDhiFZGmOSwDGuemp57eGBoatNbX14Pdbh/EDMalaYpsUYydHI9YYWTxAqzxfGjOZDKBy+UCPLxGttkfspnXznUymhxhZFEiQ18hWSIY/Ompq6sDr9cLHMftzyOjj4RQQCTZf3bdh8iw+glf/f5FjUYDwWCuVNvzGp/6rFgIgmBJp9c+ZLNZEaMSSTKZzDtU7Vj7vOv0X8Fbca73zFmTVlcLu/dUg2afHvr6B5o32qTYqHBnjiJbDoXEw43NonpvTU6TjU3cSf5SIuM6u044yzju6qOH93K3hTTZ7R1db8hfVuLDUH6qp/cBRnrz/duZH6TJpnHpRS/m3aKNNyG2sganRk+w3qSWEipKiIp2zTCCctZCKWavkb/oNAuam6JZZjpeMlkBYYJdtwSzc7X6LcAAZxVxcQ2YXyMAAAAASUVORK5CYII='
1678 img.style.pointerEvents = 'all'
1679 img.style.position = 'absolute'
1680 img.style.left = width + 'px'
1681 img.style.top = '0px'
1682 img.style.width = '20px'
1683 img.style.height = '20px'
1684 img.style.margin = '-15px 0px 0px 0px'
1685 img.style.cursor = 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABWUlEQVQ4T6WT0U3DQAyGf/sGoBuQDcjp7p2wQTsBbNCOABMAE5BuwAjpe09XJqAjlAFyRq5yIUkRKuCXSIn92f79h/DPoGl9jLFIKS0BzAEU+l1EdgBqY8zaWnvQnLZtS+/96wgQQrgD8PLDUHsADwAeAbw556oeMCwWkQ8iqpn5NaVUikhJRLcT8KYHdGO/dwkbZp7rqMOCEMKq65xffwFCCE8AltrZGFNMi7sGEcBsAB0BdLdLAM/OOe00ihhjlVK6n7yunXP1UYMQguiTmW+stc1vLjsCiMhCT/MXgN75SkTW3ns95dmRJ+gVZmZrrVXgSWy327kxphmKfATEGGcpJS1SIQ/MvJhqkc+orvTe20zvjRRjLNu2bYjoItuXiBoRKYiozLZWBzJzlacYWVkhKSX1xPV3K6hGxpjVyQrTZN2161qJyJ6Idmpra636ZRQnf+PZ8neJn9q+rxFGGvPWAAAAAElFTkSuQmCC) 8 8,default'
1686 img.addEventListener('mousedown', function (e) {
1687 // 旋转
1688 doElementRotate(e, elementObj)
1689 })
1690 div.appendChild(img)
1691 document.body.appendChild(div)
1692 if ($this.onselect) {
1693 $this.onselect(page, elementObj)
1694 }
1695 }
1696
1697 function removeElementMoveScale() {
1698 let gNode = currentSelect ? document.getElementById('g-' + currentSelect.id) : null
1699 if (gNode) {
1700 gNode.style.outline = null
1701 }
1702 currentSelect = null
1703 let element = document.getElementById('move_element_operate')
1704 element && element.remove()
1705 if ($this.onunselect) {
1706 $this.onunselect(page)
1707 }
1708 }
1709
1710 function doElementMoveScale(e, obj, idx) {
1711 // 移动缩放
1712 if (obj.type == 'tableColumn') {
1713 // table
1714 obj = idMap[idMap[obj.pid].pid]
1715 }
1716 if (!obj.point) {
1717 return
1718 }
1719 let gNode = document.getElementById('g-' + obj.id)
1720 if (!gNode) {
1721 return
1722 }
1723 let x = obj.point[0] * zoom
1724 let y = obj.point[1] * zoom
1725 let width = obj.point[2] * zoom
1726 let height = obj.point[3] * zoom
1727 let clientX = e.clientX
1728 let clientY = e.clientY
1729 let g = d3.select(gNode)
1730 let lastScale = g.scale()
1731 let scaleX = lastScale.x
1732 let scaleY = lastScale.y
1733 let rect = gNode.getBoundingClientRect()
1734 let centerX = rect.x + rect.width / 2
1735 let centerY = rect.y + rect.height / 2
1736 let lastRotate = g.rotate()
1737 if (lastRotate.rotate) {
1738 let clientXY = rotatePoint(clientX, clientY, centerX, centerY, -lastRotate.rotate)
1739 clientX = clientXY[0]
1740 clientY = clientXY[1]
1741 }
1742 let lastTranslate = g.translate()
1743 let translateX = lastTranslate.x
1744 let translateY = lastTranslate.y
1745 let changeData = { tx: 0, ty: 0, scaleX: 1, scaleY: 1 }
1746 let changeElement = (tx, ty) => {
1747 let _scaleX = 1, _scaleY = 1
1748 let originalWidth = width / scaleX
1749 let originalHeight = height / scaleY
1750 let lastTx, originalX, newTx, newScaleX, originalTranslateX
1751 let lastTy, originalY, newTy, newScaleY, originalTranslateY
1752 switch (idx) {
1753 case 0:
1754 // 左上角
1755 _scaleX = (width - tx) / width
1756 _scaleY = (height - ty) / height
1757 if (_scaleX < 0.15 || _scaleY < 0.15) {
1758 return false
1759 }
1760 changeData.tx = tx
1761 changeData.ty = ty
1762 changeData.scaleX = _scaleX
1763 changeData.scaleY = _scaleY
1764 // g.translate(translateX + tx + x * (1 - _scaleX), translateY + ty + y * (1 - _scaleY))
1765 // g.scale(scaleX * _scaleX, scaleY * _scaleY)
1766 lastTx = originalWidth - width
1767 originalX = x - lastTx
1768 newTx = x - originalX + tx
1769 newScaleX = (originalWidth - newTx) / originalWidth
1770 originalTranslateX = +(g.attr('moveX') || 0) * newScaleX
1771 lastTy = originalHeight - height
1772 originalY = y - lastTy
1773 newTy = y - originalY + ty
1774 newScaleY = (originalHeight - newTy) / originalHeight
1775 originalTranslateY = +(g.attr('moveY') || 0) * newScaleY
1776 g.translate(originalTranslateX + newTx + originalX * (1 - newScaleX), originalTranslateY + newTy + originalY * (1 - newScaleY))
1777 g.scale(newScaleX, newScaleY)
1778 break
1779 case 1:
1780 // 上
1781 _scaleY = (height - ty) / height
1782 if (_scaleY < 0.15) {
1783 return false
1784 }
1785 changeData.ty = ty
1786 changeData.scaleY = _scaleY
1787 // g.translate(translateX, translateY + ty + y * (1 - _scaleY))
1788 // g.scale(scaleX, scaleY * _scaleY)
1789 lastTy = originalHeight - height
1790 originalY = y - lastTy
1791 newTy = y - originalY + ty
1792 newScaleY = (originalHeight - newTy) / originalHeight
1793 originalTranslateY = +(g.attr('moveY') || 0) * newScaleY
1794 g.translate(translateX, originalTranslateY + newTy + originalY * (1 - newScaleY))
1795 g.scale(scaleX, newScaleY)
1796 break
1797 case 2:
1798 // 右上角
1799 _scaleX = (width + tx) / width
1800 _scaleY = (height - ty) / height
1801 if (_scaleX < 0.15 || _scaleY < 0.15) {
1802 return false
1803 }
1804 changeData.ty = ty
1805 changeData.scaleX = _scaleX
1806 changeData.scaleY = _scaleY
1807 // g.translate(translateX + x * (1 - _scaleX), translateY + ty + y * (1 - _scaleY))
1808 // g.scale(scaleX * _scaleX, scaleY * _scaleY)
1809 lastTx = width - originalWidth
1810 originalX = x
1811 newTx = lastTx + tx
1812 newScaleX = (originalWidth + newTx) / originalWidth
1813 originalTranslateX = +(g.attr('moveX') || 0) * newScaleX
1814 lastTy = originalHeight - height
1815 originalY = y - lastTy
1816 newTy = y - originalY + ty
1817 newScaleY = (originalHeight - newTy) / originalHeight
1818 originalTranslateY = +(g.attr('moveY') || 0) * newScaleY
1819 g.translate(originalTranslateX + originalX * (1 - newScaleX), originalTranslateY + newTy + originalY * (1 - newScaleY))
1820 g.scale(newScaleX, newScaleY)
1821 break
1822 case 3:
1823 // 右
1824 _scaleX = (width + tx) / width
1825 if (_scaleX < 0.15) {
1826 return false
1827 }
1828 changeData.scaleX = _scaleX
1829 // g.translate(translateX + x * (1 - _scaleX), translateY)
1830 // g.scale(scaleX * _scaleX, scaleY)
1831 lastTx = width - originalWidth
1832 originalX = x
1833 newTx = lastTx + tx
1834 newScaleX = (originalWidth + newTx) / originalWidth
1835 originalTranslateX = +(g.attr('moveX') || 0) * newScaleX
1836 g.translate(originalTranslateX + originalX * (1 - newScaleX), translateY)
1837 g.scale(newScaleX, scaleY)
1838 break
1839 case 4:
1840 // 右下角
1841 _scaleX = (width + tx) / width
1842 _scaleY = (height + ty) / height
1843 if (_scaleX < 0.15 || _scaleY < 0.15) {
1844 return false
1845 }
1846 changeData.scaleX = _scaleX
1847 changeData.scaleY = _scaleY
1848 // g.translate(translateX + x * (1 - _scaleX), translateY + y * (1 - _scaleY))
1849 // g.scale(scaleX * _scaleX, scaleY * _scaleY)
1850 lastTx = width - originalWidth
1851 originalX = x
1852 newTx = lastTx + tx
1853 newScaleX = (originalWidth + newTx) / originalWidth
1854 originalTranslateX = +(g.attr('moveX') || 0) * newScaleX
1855 lastTy = height - originalHeight
1856 originalY = y
1857 newTy = lastTy + ty
1858 newScaleY = (originalHeight + newTy) / originalHeight
1859 originalTranslateY = +(g.attr('moveY') || 0) * newScaleY
1860 g.translate(originalTranslateX + originalX * (1 - newScaleX), originalTranslateY + originalY * (1 - newScaleY))
1861 g.scale(newScaleX, newScaleY)
1862 break
1863 case 5:
1864 // 下
1865 _scaleY = (height + ty) / height
1866 if (_scaleY < 0.15) {
1867 return false
1868 }
1869 changeData.scaleY = _scaleY
1870 // g.translate(translateX, translateY + y * (1 - _scaleY))
1871 // g.scale(scaleX, scaleY * _scaleY)
1872 lastTy = height - originalHeight
1873 originalY = y
1874 newTy = lastTy + ty
1875 newScaleY = (originalHeight + newTy) / originalHeight
1876 originalTranslateY = +(g.attr('moveY') || 0) * newScaleY
1877 g.translate(translateX, originalTranslateY + originalY * (1 - newScaleY))
1878 g.scale(scaleX, newScaleY)
1879 break
1880 case 6:
1881 // 左下角
1882 _scaleX = (width - tx) / width
1883 _scaleY = (height + ty) / height
1884 if (_scaleX < 0.15 || _scaleY < 0.15) {
1885 return false
1886 }
1887 changeData.scaleX = _scaleX
1888 changeData.scaleY = _scaleY
1889 changeData.tx = tx
1890 // g.translate(translateX + tx + x * (1 - _scaleX), translateY + y * (1 - _scaleY))
1891 // g.scale(scaleX * _scaleX, scaleY * _scaleY)
1892 lastTx = originalWidth - width
1893 originalX = x - lastTx
1894 newTx = x - originalX + tx
1895 newScaleX = (originalWidth - newTx) / originalWidth
1896 originalTranslateX = +(g.attr('moveX') || 0) * newScaleX
1897 lastTy = height - originalHeight
1898 originalY = y
1899 newTy = lastTy + ty
1900 newScaleY = (originalHeight + newTy) / originalHeight
1901 originalTranslateY = +(g.attr('moveY') || 0) * newScaleY
1902 g.translate(originalTranslateX + newTx + originalX * (1 - newScaleX), originalTranslateY + originalY * (1 - newScaleY))
1903 g.scale(newScaleX, newScaleY)
1904 break
1905 case 7:
1906 // 左
1907 _scaleX = (width - tx) / width
1908 if (_scaleX < 0.15) {
1909 return false
1910 }
1911 changeData.tx = tx
1912 changeData.scaleX = _scaleX
1913 // g.translate(translateX + tx + x * (1 - _scaleX), translateY)
1914 // g.scale(scaleX * _scaleX, scaleY)
1915 lastTx = originalWidth - width
1916 originalX = x - lastTx
1917 newTx = x - originalX + tx
1918 newScaleX = (originalWidth - newTx) / originalWidth
1919 originalTranslateX = +(g.attr('moveX') || 0) * newScaleX
1920 g.translate(originalTranslateX + newTx + originalX * (1 - newScaleX), translateY)
1921 g.scale(newScaleX, scaleY)
1922 break
1923 }
1924 return true
1925 }
1926 let updateElement = (obj) => {
1927 let _tx = changeData.tx / zoom
1928 let _ty = changeData.ty / zoom
1929 let point = obj.point
1930 point[0] = point[0] + _tx
1931 point[1] = point[1] + _ty
1932 let _width = point[2]
1933 let _height = point[3]
1934 point[2] = point[2] * changeData.scaleX
1935 point[3] = point[3] * changeData.scaleY
1936 let anchor = obj.extInfo.property.anchor
1937 if (point !== anchor) {
1938 anchor[0] = anchor[0] + _tx * (anchor[2] / _width)
1939 anchor[1] = anchor[1] + _ty * (anchor[3] / _height)
1940 anchor[2] = anchor[2] * changeData.scaleX
1941 anchor[3] = anchor[3] * changeData.scaleY
1942 }
1943 let interiorAnchor = obj.extInfo.property.interiorAnchor
1944 if (interiorAnchor) {
1945 interiorAnchor[0] = interiorAnchor[0] + _tx * (interiorAnchor[2] / point[2])
1946 interiorAnchor[1] = interiorAnchor[1] + _ty * (interiorAnchor[3] / point[3])
1947 }
1948 if (obj.type == 'table') {
1949 let rows = obj.children || []
1950 for (let i = 0; i < rows.length; i++) {
1951 let row_property = rows[i].extInfo.property
1952 row_property.rowHeight *= changeData.scaleY
1953 let columns = rows[i].children || []
1954 for (let j = 0; j < columns.length; j++) {
1955 let columns_property = columns[j].extInfo.property
1956 columns_property.columnWidth *= changeData.scaleX
1957 let columnAnchor = columns[j].extInfo.property.anchor
1958 columnAnchor[0] = anchor[0] + columns_property.columnWidth * j + (j + 1) * changeData.scaleX
1959 columnAnchor[1] = anchor[1] + row_property.rowHeight * i + (i + 1) * changeData.scaleY
1960 columnAnchor[2] = columns_property.columnWidth
1961 columnAnchor[3] = row_property.rowHeight
1962 }
1963 }
1964 }
1965 }
1966 let move_element_operate = document.getElementById('move_element_operate')
1967 let tx = 0, ty = 0
1968 document.onmousemove = function (e2) {
1969 let clientX2 = e2.clientX
1970 let clientY2 = e2.clientY
1971 if (lastRotate.rotate) {
1972 let clientXY2 = rotatePoint(clientX2, clientY2, centerX, centerY, -lastRotate.rotate)
1973 clientX2 = clientXY2[0]
1974 clientY2 = clientXY2[1]
1975 }
1976 tx = clientX2 - clientX
1977 ty = clientY2 - clientY
1978 if (move_element_operate) {
1979 move_element_operate.remove()
1980 move_element_operate = null
1981 currentSelect = null
1982 }
1983 let change = changeElement(tx, ty)
1984 if (!change) {
1985 e.preventDefault()
1986 }
1987 }
1988 document.onmouseup = function (e2) {
1989 document.onmousemove = null
1990 document.onmouseup = null
1991 if (tx || ty) {
1992 updateElement(obj)
1993 if (obj != idMap[obj.id]) {
1994 updateElement(idMap[obj.id])
1995 }
1996 // 重新渲染
1997 // $this.drawPptx(pptx, pageIndex, obj.id)
1998 // 重新渲染元素
1999 $this.redrawElementWithId(obj.id)
2000 if ($this.onchange) {
2001 $this.onchange(page, 'scale', obj, e2)
2002 }
2003 } else {
2004 addElementMoveScale(obj)
2005 }
2006 }
2007 }
2008
2009 function editRunText(rId, textObj) {
2010 if (mode != 'edit') {
2011 return
2012 }
2013 let obj = idMap[rId]
2014 if (obj == null) {
2015 // ignore master element
2016 return
2017 }
2018 addElementMoveScale(textObj)
2019 let rect = null
2020 let nodeStyle = null
2021 let nodes = document.querySelectorAll(`tspan[id='${rId}']`)
2022 for (let i = 0; i < nodes.length; i++) {
2023 let _rect = nodes[i].getBoundingClientRect()
2024 if (_rect.width == 0 || _rect.height == 0) {
2025 continue
2026 }
2027 if (rect == null) {
2028 rect = { x: _rect.x, y: _rect.y, endX: _rect.x + _rect.width, endY: _rect.y + _rect.height }
2029 nodeStyle = window.getComputedStyle(nodes[i])
2030 } else {
2031 rect.x = Math.min(rect.x, _rect.x)
2032 rect.y = Math.min(rect.y, _rect.y)
2033 rect.endX = Math.max(rect.endX, _rect.x + _rect.width)
2034 rect.endY = Math.max(rect.endY, _rect.y + _rect.height)
2035 }
2036 }
2037 if (rect == null) {
2038 return
2039 }
2040 rect.width = rect.endX - rect.x
2041 rect.height = rect.endY - rect.y
2042 let fontSize = +nodeStyle.fontSize.replace('px', '')
2043 let textarea = document.getElementById('textarea_' + rId)
2044 if (textarea) {
2045 textarea.remove()
2046 }
2047 textarea = document.createElement('textarea')
2048 textarea.id = 'textarea_' + rId
2049 textarea.style.position = 'absolute'
2050 textarea.style.background = 'none'
2051 textarea.style.border = 'none'
2052 textarea.style.padding = '0'
2053 textarea.style.margin = '0'
2054 textarea.style.resize = 'none'
2055 textarea.style.overflow = 'hidden'
2056 textarea.style.outline = 'none'
2057 textarea.style.zIndex = 999
2058 let scrollY = window.scrollY || document.documentElement.scrollTop
2059 let scrollX = window.scrollX || document.documentElement.scrollLeft
2060 textarea.style.left = (rect.x + scrollX) + 'px'
2061 textarea.style.top = (rect.y + scrollY) + 'px'
2062 let textWordWrap = textObj.extInfo.property.textWordWrap ?? true
2063 textarea.style.width = Math.max(scaleValue(textObj.point[2]) || rect.width, fontSize) + 'px'
2064 textarea.style.height = Math.max(scaleValue(textObj.point[3]) || rect.height, fontSize) + 'px'
2065 let rotation = (textObj.extInfo.property || {}).rotation
2066 if (rotation) {
2067 textarea.style.transformOrigin = 'center'
2068 textarea.style.transform = 'rotate(' + rotation + 'deg)'
2069 }
2070 if (nodeStyle.fill && nodeStyle.fill.startsWith('rgb')) {
2071 textarea.style.color = nodeStyle.fill
2072 }
2073 textarea.style.font = nodeStyle.font
2074 textarea.style.fontSize = fontSize + 'px'
2075 let p_property = textObj.children[0]?.extInfo.property
2076 // let lineSpacing = p_property.lineSpacing > 0 ? (p_property.lineSpacing / 100) : (1 + Math.abs(p_property.lineSpacing || 0) / 100)
2077 // if (lineSpacing && lineSpacing != 1) {
2078 // textarea.style.lineHeight = lineSpacing
2079 // }
2080 textarea.value = obj.text
2081 nodes.forEach(s => s.style.visibility = 'hidden')
2082 document.body.appendChild(textarea)
2083 textarea.addEventListener('blur', function () {
2084 if (obj.text == textarea.value) {
2085 textarea.remove()
2086 nodes.forEach(s => s.style.visibility = null)
2087 return
2088 }
2089 obj.text = textarea.value
2090 recursion(textObj.children, c => {
2091 if (c.id == rId) {
2092 c.text = obj.text
2093 }
2094 })
2095 textarea.remove()
2096 let gNode = document.getElementById('g-' + textObj.id)
2097 let g = d3.select(gNode)
2098 drawTextWithG(g, textObj)
2099 if ($this.onchange) {
2100 $this.onchange(textObj)
2101 }
2102 })
2103 textarea.addEventListener('focus', function () {
2104 if (textarea.scrollHeight > textarea.clientHeight) {
2105 if (textWordWrap) {
2106 textarea.style.height = textarea.scrollHeight + 'px'
2107 } else {
2108 textarea.style.width = textarea.clientWidth + fontSize + 'px'
2109 }
2110 } else if (textarea.scrollWidth > textarea.clientWidth) {
2111 if (textWordWrap) {
2112 textarea.style.height = textarea.clientWidth + fontSize + 'px'
2113 } else {
2114 textarea.style.width = textarea.scrollWidth + 'px'
2115 }
2116 }
2117 })
2118 textarea.addEventListener('input', function () {
2119 if (textarea.scrollHeight > textarea.clientHeight) {
2120 if (textWordWrap) {
2121 textarea.style.height = textarea.scrollHeight + 'px'
2122 if (textObj.extInfo.property.textVerticalAlignment == 'BOTTOM') {
2123 textarea.style.top = +textarea.style.top.replace('px', '') - fontSize + 'px'
2124 } else if (textObj.extInfo.property.textVerticalAlignment == 'MIDDLE') {
2125 textarea.style.top = +textarea.style.top.replace('px', '') - fontSize / 2 + 'px'
2126 }
2127 } else {
2128 textarea.style.width = textarea.clientWidth + fontSize + 'px'
2129 if (p_property.textAlign == 'RIGHT') {
2130 textarea.style.left = +textarea.style.left.replace('px', '') - fontSize + 'px'
2131 } else if (p_property.textAlign == 'CENTER') {
2132 textarea.style.left = +textarea.style.left.replace('px', '') - fontSize / 2 + 'px'
2133 }
2134 }
2135 } else if (textarea.scrollWidth > textarea.clientWidth) {
2136 if (!textWordWrap) {
2137 textarea.style.width = textarea.scrollWidth + 'px'
2138 if (p_property.textAlign == 'RIGHT') {
2139 textarea.style.left = +textarea.style.left.replace('px', '') - fontSize + 'px'
2140 } else if (p_property.textAlign == 'CENTER') {
2141 textarea.style.left = +textarea.style.left.replace('px', '') - fontSize / 2 + 'px'
2142 }
2143 }
2144 }
2145 })
2146 textarea.focus()
2147 }
2148
2149 this.idMap = idMap
2150 this.recursion = recursion
2151 this.drawGeometry = drawGeometry
2152 this.drawElement = drawElement
2153 this.toPaint = toPaint
2154 this.toColor = toColor
2155 this.toColorValue = toColorValue
2156 this.loadImage = loadImage
2157 this.text = text
2158 this.scaleValue = scaleValue
2159 this.showPoint = showPoint
2160 this.removePoint = removePoint
2161 this.addElementMoveScale = addElementMoveScale
2162 this.removeElementMoveScale = removeElementMoveScale
2163
2164 d3.addEventListener('document', 'mousemove', function(event) {
2165 if (!pptx) {
2166 removePoint()
2167 return
2168 }
2169 let svgNode = svg.node()
2170 let svgRect = svgNode.getClientRects()[0]
2171 if (!svgRect) {
2172 return
2173 }
2174 let clientX = event.clientX
2175 let clientY = event.clientY
2176 if (!(clientX >= svgRect.x && clientX <= svgRect.x + svgRect.width && clientY >= svgRect.y && clientY <= svgRect.y + svgRect.height)) {
2177 removePoint()
2178 return
2179 }
2180 let offsetX = clientX - svgRect.x
2181 let offsetY = clientY - svgRect.y
2182 mTimer && clearTimeout(mTimer)
2183 mTimer = setTimeout(() => {
2184 let newPointList = []
2185 for (let i = 0; i < pointList.length; i++) {
2186 let point = pointList[i]
2187 if (offsetX >= point.x && offsetX <= point.endX && offsetY >= point.y && offsetY <= point.endY) {
2188 // if (currentPoint == point) return
2189 // showPoint(point)
2190 // return
2191 newPointList.push(point)
2192 }
2193 }
2194 if (newPointList.length > 0) {
2195 newPointList.sort((p1, p2) => {
2196 let text1 = p1.obj.type == 'text' || p1.obj.type == 'freeform'
2197 let text2 = p2.obj.type == 'text' || p2.obj.type == 'freeform'
2198 if (!text1 && !text2) {
2199 return 0
2200 }
2201 let s1 = text(p1.obj)
2202 let s2 = text(p2.obj)
2203 if (s1 && s2) {
2204 return 0
2205 }
2206 if (s1) {
2207 return -1
2208 }
2209 if (s2) {
2210 return 1
2211 }
2212 return 0
2213 })
2214 showPoint(newPointList[0])
2215 } else {
2216 removePoint()
2217 }
2218 }, 10)
2219 })
2220 d3.addEventListener('document', 'keydown', function (event) {
2221 if (!currentPoint) {
2222 return
2223 }
2224 let obj = currentPoint.obj
2225 if (!obj.point || obj.type == 'tableColumn') {
2226 return
2227 }
2228 let gNode = document.getElementById('g-' + obj.id)
2229 if (!gNode) {
2230 return
2231 }
2232 if (event.keyCode == 46) {
2233 recursion(page.children, element => {
2234 if (obj.id == element.id) {
2235 const children = element.pid ? idMap[element.pid].children : page.children
2236 for (let i = 0; i < children.length; i++) {
2237 if (children[i].id == element.id) {
2238 children.splice(i, 1)
2239 delete idMap[obj.id]
2240 gNode.remove()
2241 currentPoint = null
2242 removeElementMoveScale()
2243 calcPointList(page)
2244 if ($this.onchange) {
2245 $this.onchange(page, 'delete', obj, event)
2246 }
2247 break
2248 }
2249 }
2250 }
2251 })
2252 }
2253 })
2254 d3.addEventListener('window', 'scroll', function() {
2255 removePoint()
2256 removeElementMoveScale()
2257 })
2258 d3.addEventListener('window', 'resize', function() {
2259 removePoint()
2260 removeElementMoveScale()
2261 page && calcPointList(page)
2262 })
2263 }
2264
2265 // export { D3Element, d3, Ppt2Svg }
2265 lines JAVASCRIPT