Get rid of CSS transform on each annotation in the annotation layer
- each annotation has its coordinates/dimensions expressed in percentage, hence it's correctly positioned whatever the scale factor is; - the font sizes are expressed in percentage too and the main font size is scaled thanks a css var (--scale-factor); - the rotation is now applied on the div annotationLayer; - this patch improve the rendering of some strings where the glyph spacing was not correct (it's a Firefox bug); - it helps to simplify the code and it should slightly improve the update of page (on zoom or rotation).
This commit is contained in:
		
							parent
							
								
									6dc8d1f532
								
							
						
					
					
						commit
						e2db9bacef
					
				| @ -432,6 +432,7 @@ class Annotation { | |||||||
|     this.setAppearance(dict); |     this.setAppearance(dict); | ||||||
|     this.setBorderAndBackgroundColors(dict.get("MK")); |     this.setBorderAndBackgroundColors(dict.get("MK")); | ||||||
| 
 | 
 | ||||||
|  |     this._hasOwnCanvas = false; | ||||||
|     this._streams = []; |     this._streams = []; | ||||||
|     if (this.appearance) { |     if (this.appearance) { | ||||||
|       this._streams.push(this.appearance); |       this._streams.push(this.appearance); | ||||||
| @ -450,7 +451,6 @@ class Annotation { | |||||||
|       modificationDate: this.modificationDate, |       modificationDate: this.modificationDate, | ||||||
|       rect: this.rectangle, |       rect: this.rectangle, | ||||||
|       subtype: params.subtype, |       subtype: params.subtype, | ||||||
|       hasOwnCanvas: false, |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     if (params.collectFields) { |     if (params.collectFields) { | ||||||
| @ -849,7 +849,7 @@ class Annotation { | |||||||
|     const data = this.data; |     const data = this.data; | ||||||
|     let appearance = this.appearance; |     let appearance = this.appearance; | ||||||
|     const isUsingOwnCanvas = |     const isUsingOwnCanvas = | ||||||
|       data.hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY; |       this._hasOwnCanvas && intent & RenderingIntentFlag.DISPLAY; | ||||||
|     if (!appearance) { |     if (!appearance) { | ||||||
|       if (!isUsingOwnCanvas) { |       if (!isUsingOwnCanvas) { | ||||||
|         return Promise.resolve(new OperatorList()); |         return Promise.resolve(new OperatorList()); | ||||||
| @ -2163,7 +2163,7 @@ class ButtonWidgetAnnotation extends WidgetAnnotation { | |||||||
|     } else if (this.data.radioButton) { |     } else if (this.data.radioButton) { | ||||||
|       this._processRadioButton(params); |       this._processRadioButton(params); | ||||||
|     } else if (this.data.pushButton) { |     } else if (this.data.pushButton) { | ||||||
|       this.data.hasOwnCanvas = true; |       this._hasOwnCanvas = true; | ||||||
|       this._processPushButton(params); |       this._processPushButton(params); | ||||||
|     } else { |     } else { | ||||||
|       warn("Invalid field flags for button widget annotation"); |       warn("Invalid field flags for button widget annotation"); | ||||||
|  | |||||||
| @ -1425,7 +1425,7 @@ class ChoiceList extends XFAObject { | |||||||
|     const field = ui[$getParent](); |     const field = ui[$getParent](); | ||||||
|     const fontSize = (field.font && field.font.size) || 10; |     const fontSize = (field.font && field.font.size) || 10; | ||||||
|     const optionStyle = { |     const optionStyle = { | ||||||
|       fontSize: `calc(${fontSize}px * var(--zoom-factor))`, |       fontSize: `calc(${fontSize}px * var(--scale-factor))`, | ||||||
|     }; |     }; | ||||||
|     const children = []; |     const children = []; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -182,6 +182,10 @@ function mapStyle(styleStr, node, richText) { | |||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   if (richText && style.fontSize) { | ||||||
|  |     style.fontSize = `calc(${style.fontSize} * var(--scale-factor))`; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   fixTextIndent(style); |   fixTextIndent(style); | ||||||
|   return style; |   return style; | ||||||
| } | } | ||||||
|  | |||||||
| @ -197,7 +197,11 @@ class AnnotationElement { | |||||||
|       page = this.page, |       page = this.page, | ||||||
|       viewport = this.viewport; |       viewport = this.viewport; | ||||||
|     const container = document.createElement("section"); |     const container = document.createElement("section"); | ||||||
|     let { width, height } = getRectDims(data.rect); |     const { width, height } = getRectDims(data.rect); | ||||||
|  | 
 | ||||||
|  |     const [pageLLx, pageLLy, pageURx, pageURy] = viewport.viewBox; | ||||||
|  |     const pageWidth = pageURx - pageLLx; | ||||||
|  |     const pageHeight = pageURy - pageLLy; | ||||||
| 
 | 
 | ||||||
|     container.setAttribute("data-annotation-id", data.id); |     container.setAttribute("data-annotation-id", data.id); | ||||||
| 
 | 
 | ||||||
| @ -210,41 +214,13 @@ class AnnotationElement { | |||||||
|       page.view[3] - data.rect[3] + page.view[1], |       page.view[3] - data.rect[3] + page.view[1], | ||||||
|     ]); |     ]); | ||||||
| 
 | 
 | ||||||
|     if (data.hasOwnCanvas) { |  | ||||||
|       const transform = viewport.transform.slice(); |  | ||||||
|       const [scaleX, scaleY] = Util.singularValueDecompose2dScale(transform); |  | ||||||
|       width = Math.ceil(width * scaleX); |  | ||||||
|       height = Math.ceil(height * scaleY); |  | ||||||
|       rect[0] *= scaleX; |  | ||||||
|       rect[1] *= scaleY; |  | ||||||
|       // Reset the scale part of the transform matrix (which must be diagonal
 |  | ||||||
|       // or anti-diagonal) in order to avoid to rescale the canvas.
 |  | ||||||
|       // The canvas for the annotation is correctly scaled when it is drawn
 |  | ||||||
|       // (see `beginAnnotation` in canvas.js).
 |  | ||||||
|       for (let i = 0; i < 4; i++) { |  | ||||||
|         transform[i] = Math.sign(transform[i]); |  | ||||||
|       } |  | ||||||
|       container.style.transform = `matrix(${transform.join(",")})`; |  | ||||||
|     } else { |  | ||||||
|       container.style.transform = `matrix(${viewport.transform.join(",")})`; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     container.style.transformOrigin = `${-rect[0]}px ${-rect[1]}px`; |  | ||||||
| 
 |  | ||||||
|     if (!ignoreBorder && data.borderStyle.width > 0) { |     if (!ignoreBorder && data.borderStyle.width > 0) { | ||||||
|       container.style.borderWidth = `${data.borderStyle.width}px`; |       container.style.borderWidth = `${data.borderStyle.width}px`; | ||||||
|       if (data.borderStyle.style !== AnnotationBorderStyleType.UNDERLINE) { |  | ||||||
|         // Underline styles only have a bottom border, so we do not need
 |  | ||||||
|         // to adjust for all borders. This yields a similar result as
 |  | ||||||
|         // Adobe Acrobat/Reader.
 |  | ||||||
|         width -= 2 * data.borderStyle.width; |  | ||||||
|         height -= 2 * data.borderStyle.width; |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       const horizontalRadius = data.borderStyle.horizontalCornerRadius; |       const horizontalRadius = data.borderStyle.horizontalCornerRadius; | ||||||
|       const verticalRadius = data.borderStyle.verticalCornerRadius; |       const verticalRadius = data.borderStyle.verticalCornerRadius; | ||||||
|       if (horizontalRadius > 0 || verticalRadius > 0) { |       if (horizontalRadius > 0 || verticalRadius > 0) { | ||||||
|         const radius = `${horizontalRadius}px / ${verticalRadius}px`; |         const radius = `calc(${horizontalRadius}px * var(--scale-factor)) / calc(${verticalRadius}px * var(--scale-factor))`; | ||||||
|         container.style.borderRadius = radius; |         container.style.borderRadius = radius; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
| @ -286,15 +262,11 @@ class AnnotationElement { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     container.style.left = `${rect[0]}px`; |     container.style.left = `${(100 * (rect[0] - pageLLx)) / pageWidth}%`; | ||||||
|     container.style.top = `${rect[1]}px`; |     container.style.top = `${(100 * (rect[1] - pageLLy)) / pageHeight}%`; | ||||||
|  |     container.style.width = `${(100 * width) / pageWidth}%`; | ||||||
|  |     container.style.height = `${(100 * height) / pageHeight}%`; | ||||||
| 
 | 
 | ||||||
|     if (data.hasOwnCanvas) { |  | ||||||
|       container.style.width = container.style.height = "auto"; |  | ||||||
|     } else { |  | ||||||
|       container.style.width = `${width}px`; |  | ||||||
|       container.style.height = `${height}px`; |  | ||||||
|     } |  | ||||||
|     return container; |     return container; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -464,7 +436,7 @@ class AnnotationElement { | |||||||
|     const popup = popupElement.render(); |     const popup = popupElement.render(); | ||||||
| 
 | 
 | ||||||
|     // Position the popup next to the annotation's container.
 |     // Position the popup next to the annotation's container.
 | ||||||
|     popup.style.left = container.style.width; |     popup.style.left = "100%"; | ||||||
| 
 | 
 | ||||||
|     container.append(popup); |     container.append(popup); | ||||||
|   } |   } | ||||||
| @ -820,8 +792,6 @@ class TextAnnotationElement extends AnnotationElement { | |||||||
|     this.container.className = "textAnnotation"; |     this.container.className = "textAnnotation"; | ||||||
| 
 | 
 | ||||||
|     const image = document.createElement("img"); |     const image = document.createElement("img"); | ||||||
|     image.style.height = this.container.style.height; |  | ||||||
|     image.style.width = this.container.style.width; |  | ||||||
|     image.src = |     image.src = | ||||||
|       this.imageResourcesPath + |       this.imageResourcesPath + | ||||||
|       "annotation-" + |       "annotation-" + | ||||||
| @ -925,21 +895,20 @@ class WidgetAnnotationElement extends AnnotationElement { | |||||||
|     // it's instead based on the field height.
 |     // it's instead based on the field height.
 | ||||||
|     // If the height is "big" then it could lead to a too big font size
 |     // If the height is "big" then it could lead to a too big font size
 | ||||||
|     // so in this case use the one we've in the pdf (hence the min).
 |     // so in this case use the one we've in the pdf (hence the min).
 | ||||||
|  |     let computedFontSize; | ||||||
|     if (this.data.multiLine) { |     if (this.data.multiLine) { | ||||||
|       const height = Math.abs(this.data.rect[3] - this.data.rect[1]); |       const height = Math.abs(this.data.rect[3] - this.data.rect[1]); | ||||||
|       const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; |       const numberOfLines = Math.round(height / (LINE_FACTOR * fontSize)) || 1; | ||||||
|       const lineHeight = height / numberOfLines; |       const lineHeight = height / numberOfLines; | ||||||
|       style.fontSize = `${Math.min( |       computedFontSize = Math.min( | ||||||
|         fontSize, |         fontSize, | ||||||
|         Math.round(lineHeight / LINE_FACTOR) |         Math.round(lineHeight / LINE_FACTOR) | ||||||
|       )}px`;
 |       ); | ||||||
|     } else { |     } else { | ||||||
|       const height = Math.abs(this.data.rect[3] - this.data.rect[1]); |       const height = Math.abs(this.data.rect[3] - this.data.rect[1]); | ||||||
|       style.fontSize = `${Math.min( |       computedFontSize = Math.min(fontSize, Math.round(height / LINE_FACTOR)); | ||||||
|         fontSize, |  | ||||||
|         Math.round(height / LINE_FACTOR) |  | ||||||
|       )}px`;
 |  | ||||||
|     } |     } | ||||||
|  |     style.fontSize = `${computedFontSize}%`; | ||||||
| 
 | 
 | ||||||
|     style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); |     style.color = Util.makeHexColor(fontColor[0], fontColor[1], fontColor[2]); | ||||||
| 
 | 
 | ||||||
| @ -1227,7 +1196,7 @@ class TextWidgetAnnotationElement extends WidgetAnnotationElement { | |||||||
|         const combWidth = fieldWidth / this.data.maxLen; |         const combWidth = fieldWidth / this.data.maxLen; | ||||||
| 
 | 
 | ||||||
|         element.classList.add("comb"); |         element.classList.add("comb"); | ||||||
|         element.style.letterSpacing = `calc(${combWidth}px - 1ch)`; |         element.style.letterSpacing = `calc(${combWidth}px * var(--scale-factor) - 1ch)`; | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
|       element = document.createElement("div"); |       element = document.createElement("div"); | ||||||
| @ -1464,10 +1433,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { | |||||||
|       value: this.data.fieldValue, |       value: this.data.fieldValue, | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const fontSize = |  | ||||||
|       this.data.defaultAppearanceData.fontSize || DEFAULT_FONT_SIZE; |  | ||||||
|     const fontSizeStyle = `calc(${fontSize}px * var(--zoom-factor))`; |  | ||||||
| 
 |  | ||||||
|     const selectElement = document.createElement("select"); |     const selectElement = document.createElement("select"); | ||||||
|     GetElementsByNameSet.add(selectElement); |     GetElementsByNameSet.add(selectElement); | ||||||
|     selectElement.setAttribute("data-element-id", id); |     selectElement.setAttribute("data-element-id", id); | ||||||
| @ -1499,9 +1464,6 @@ class ChoiceWidgetAnnotationElement extends WidgetAnnotationElement { | |||||||
|       const optionElement = document.createElement("option"); |       const optionElement = document.createElement("option"); | ||||||
|       optionElement.textContent = option.displayValue; |       optionElement.textContent = option.displayValue; | ||||||
|       optionElement.value = option.exportValue; |       optionElement.value = option.exportValue; | ||||||
|       if (this.data.combo) { |  | ||||||
|         optionElement.style.fontSize = fontSizeStyle; |  | ||||||
|       } |  | ||||||
|       if (storedData.value.includes(option.exportValue)) { |       if (storedData.value.includes(option.exportValue)) { | ||||||
|         optionElement.setAttribute("selected", true); |         optionElement.setAttribute("selected", true); | ||||||
|         addAnEmptyEntry = false; |         addAnEmptyEntry = false; | ||||||
| @ -1749,9 +1711,12 @@ class PopupAnnotationElement extends AnnotationElement { | |||||||
|       rect[0] + this.data.parentRect[2] - this.data.parentRect[0]; |       rect[0] + this.data.parentRect[2] - this.data.parentRect[0]; | ||||||
|     const popupTop = rect[1]; |     const popupTop = rect[1]; | ||||||
| 
 | 
 | ||||||
|     this.container.style.transformOrigin = `${-popupLeft}px ${-popupTop}px`; |     const [pageLLx, pageLLy, pageURx, pageURy] = this.viewport.viewBox; | ||||||
|     this.container.style.left = `${popupLeft}px`; |     const pageWidth = pageURx - pageLLx; | ||||||
|     this.container.style.top = `${popupTop}px`; |     const pageHeight = pageURy - pageLLy; | ||||||
|  | 
 | ||||||
|  |     this.container.style.left = `${(100 * (popupLeft - pageLLx)) / pageWidth}%`; | ||||||
|  |     this.container.style.top = `${(100 * (popupTop - pageLLy)) / pageHeight}%`; | ||||||
| 
 | 
 | ||||||
|     this.container.append(popup.render()); |     this.container.append(popup.render()); | ||||||
|     return this.container; |     return this.container; | ||||||
| @ -1961,7 +1926,11 @@ class LineAnnotationElement extends AnnotationElement { | |||||||
|     // trigger the popup, not the entire container.
 |     // trigger the popup, not the entire container.
 | ||||||
|     const data = this.data; |     const data = this.data; | ||||||
|     const { width, height } = getRectDims(data.rect); |     const { width, height } = getRectDims(data.rect); | ||||||
|     const svg = this.svgFactory.create(width, height); |     const svg = this.svgFactory.create( | ||||||
|  |       width, | ||||||
|  |       height, | ||||||
|  |       /* skipDimensions = */ true | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     // PDF coordinates are calculated from a bottom left origin, so transform
 |     // PDF coordinates are calculated from a bottom left origin, so transform
 | ||||||
|     // the line coordinates to a top left origin for the SVG element.
 |     // the line coordinates to a top left origin for the SVG element.
 | ||||||
| @ -2006,7 +1975,11 @@ class SquareAnnotationElement extends AnnotationElement { | |||||||
|     // popup, not the entire container.
 |     // popup, not the entire container.
 | ||||||
|     const data = this.data; |     const data = this.data; | ||||||
|     const { width, height } = getRectDims(data.rect); |     const { width, height } = getRectDims(data.rect); | ||||||
|     const svg = this.svgFactory.create(width, height); |     const svg = this.svgFactory.create( | ||||||
|  |       width, | ||||||
|  |       height, | ||||||
|  |       /* skipDimensions = */ true | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     // The browser draws half of the borders inside the square and half of
 |     // The browser draws half of the borders inside the square and half of
 | ||||||
|     // the borders outside the square by default. This behavior cannot be
 |     // the borders outside the square by default. This behavior cannot be
 | ||||||
| @ -2053,7 +2026,11 @@ class CircleAnnotationElement extends AnnotationElement { | |||||||
|     // popup, not the entire container.
 |     // popup, not the entire container.
 | ||||||
|     const data = this.data; |     const data = this.data; | ||||||
|     const { width, height } = getRectDims(data.rect); |     const { width, height } = getRectDims(data.rect); | ||||||
|     const svg = this.svgFactory.create(width, height); |     const svg = this.svgFactory.create( | ||||||
|  |       width, | ||||||
|  |       height, | ||||||
|  |       /* skipDimensions = */ true | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     // The browser draws half of the borders inside the circle and half of
 |     // The browser draws half of the borders inside the circle and half of
 | ||||||
|     // the borders outside the circle by default. This behavior cannot be
 |     // the borders outside the circle by default. This behavior cannot be
 | ||||||
| @ -2103,7 +2080,11 @@ class PolylineAnnotationElement extends AnnotationElement { | |||||||
|     // popup, not the entire container.
 |     // popup, not the entire container.
 | ||||||
|     const data = this.data; |     const data = this.data; | ||||||
|     const { width, height } = getRectDims(data.rect); |     const { width, height } = getRectDims(data.rect); | ||||||
|     const svg = this.svgFactory.create(width, height); |     const svg = this.svgFactory.create( | ||||||
|  |       width, | ||||||
|  |       height, | ||||||
|  |       /* skipDimensions = */ true | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     // Convert the vertices array to a single points string that the SVG
 |     // Convert the vertices array to a single points string that the SVG
 | ||||||
|     // polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are
 |     // polyline element expects ("x1,y1 x2,y2 ..."). PDF coordinates are
 | ||||||
| @ -2191,7 +2172,11 @@ class InkAnnotationElement extends AnnotationElement { | |||||||
|     // trigger for the popup.
 |     // trigger for the popup.
 | ||||||
|     const data = this.data; |     const data = this.data; | ||||||
|     const { width, height } = getRectDims(data.rect); |     const { width, height } = getRectDims(data.rect); | ||||||
|     const svg = this.svgFactory.create(width, height); |     const svg = this.svgFactory.create( | ||||||
|  |       width, | ||||||
|  |       height, | ||||||
|  |       /* skipDimensions = */ true | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     for (const inkList of data.inkLists) { |     for (const inkList of data.inkLists) { | ||||||
|       // Convert the ink list to a single points string that the SVG
 |       // Convert the ink list to a single points string that the SVG
 | ||||||
| @ -2515,55 +2500,27 @@ class AnnotationLayer { | |||||||
|    * @memberof AnnotationLayer |    * @memberof AnnotationLayer | ||||||
|    */ |    */ | ||||||
|   static update(parameters) { |   static update(parameters) { | ||||||
|     const { page, viewport, annotations, annotationCanvasMap, div } = |     const { annotationCanvasMap, div } = parameters; | ||||||
|       parameters; |  | ||||||
|     const transform = viewport.transform; |  | ||||||
|     const matrix = `matrix(${transform.join(",")})`; |  | ||||||
| 
 |  | ||||||
|     let scale, ownMatrix; |  | ||||||
|     for (const data of annotations) { |  | ||||||
|       const elements = div.querySelectorAll( |  | ||||||
|         `[data-annotation-id="${data.id}"]` |  | ||||||
|       ); |  | ||||||
|       if (elements) { |  | ||||||
|         for (const element of elements) { |  | ||||||
|           if (data.hasOwnCanvas) { |  | ||||||
|             const rect = Util.normalizeRect([ |  | ||||||
|               data.rect[0], |  | ||||||
|               page.view[3] - data.rect[1] + page.view[1], |  | ||||||
|               data.rect[2], |  | ||||||
|               page.view[3] - data.rect[3] + page.view[1], |  | ||||||
|             ]); |  | ||||||
| 
 |  | ||||||
|             if (!ownMatrix) { |  | ||||||
|               // When an annotation has its own canvas, then
 |  | ||||||
|               // the scale has been already applied to the canvas,
 |  | ||||||
|               // so we musn't scale it twice.
 |  | ||||||
|               scale = Math.abs(transform[0] || transform[1]); |  | ||||||
|               const ownTransform = transform.slice(); |  | ||||||
|               for (let i = 0; i < 4; i++) { |  | ||||||
|                 ownTransform[i] = Math.sign(ownTransform[i]); |  | ||||||
|               } |  | ||||||
|               ownMatrix = `matrix(${ownTransform.join(",")})`; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             const left = rect[0] * scale; |  | ||||||
|             const top = rect[1] * scale; |  | ||||||
|             element.style.left = `${left}px`; |  | ||||||
|             element.style.top = `${top}px`; |  | ||||||
|             element.style.transformOrigin = `${-left}px ${-top}px`; |  | ||||||
|             element.style.transform = ownMatrix; |  | ||||||
|           } else { |  | ||||||
|             element.style.transform = matrix; |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     this.#setAnnotationCanvasMap(div, annotationCanvasMap); |     this.#setAnnotationCanvasMap(div, annotationCanvasMap); | ||||||
|     div.hidden = false; |     div.hidden = false; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   static setDimensions(div, viewport) { | ||||||
|  |     const { width, height, rotation } = viewport; | ||||||
|  |     const { style } = div; | ||||||
|  | 
 | ||||||
|  |     if (rotation === 0 || rotation === 180) { | ||||||
|  |       style.width = `${width}px`; | ||||||
|  |       style.height = `${height}px`; | ||||||
|  |     } else { | ||||||
|  |       style.width = `${height}px`; | ||||||
|  |       style.height = `${width}px`; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     div.setAttribute("data-annotation-rotation", rotation); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   static #setAnnotationCanvasMap(div, annotationCanvasMap) { |   static #setAnnotationCanvasMap(div, annotationCanvasMap) { | ||||||
|     if (!annotationCanvasMap) { |     if (!annotationCanvasMap) { | ||||||
|       return; |       return; | ||||||
|  | |||||||
| @ -143,14 +143,18 @@ class BaseSVGFactory { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   create(width, height) { |   create(width, height, skipDimensions = false) { | ||||||
|     if (width <= 0 || height <= 0) { |     if (width <= 0 || height <= 0) { | ||||||
|       throw new Error("Invalid SVG dimensions"); |       throw new Error("Invalid SVG dimensions"); | ||||||
|     } |     } | ||||||
|     const svg = this._createSVG("svg:svg"); |     const svg = this._createSVG("svg:svg"); | ||||||
|     svg.setAttribute("version", "1.1"); |     svg.setAttribute("version", "1.1"); | ||||||
|  | 
 | ||||||
|  |     if (!skipDimensions) { | ||||||
|       svg.setAttribute("width", `${width}px`); |       svg.setAttribute("width", `${width}px`); | ||||||
|       svg.setAttribute("height", `${height}px`); |       svg.setAttribute("height", `${height}px`); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     svg.setAttribute("preserveAspectRatio", "none"); |     svg.setAttribute("preserveAspectRatio", "none"); | ||||||
|     svg.setAttribute("viewBox", `0 0 ${width} ${height}`); |     svg.setAttribute("viewBox", `0 0 ${width} ${height}`); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3021,9 +3021,6 @@ class CanvasGraphics { | |||||||
|           canvasHeight |           canvasHeight | ||||||
|         ); |         ); | ||||||
|         const { canvas, context } = this.annotationCanvas; |         const { canvas, context } = this.annotationCanvas; | ||||||
|         const viewportScaleFactorStr = `var(--zoom-factor) * ${PixelsPerInch.PDF_TO_CSS_UNITS}`; |  | ||||||
|         canvas.style.width = `calc(${width}px * ${viewportScaleFactorStr})`; |  | ||||||
|         canvas.style.height = `calc(${height}px * ${viewportScaleFactorStr})`; |  | ||||||
|         this.annotationCanvasMap.set(id, canvas); |         this.annotationCanvasMap.set(id, canvas); | ||||||
|         this.annotationCanvas.savedCtx = this.ctx; |         this.annotationCanvas.savedCtx = this.ctx; | ||||||
|         this.ctx = context; |         this.ctx = context; | ||||||
|  | |||||||
| @ -24,7 +24,6 @@ import { AnnotationEditorType, Util } from "../../shared/util.js"; | |||||||
| import { bindEvents, KeyboardManager } from "./tools.js"; | import { bindEvents, KeyboardManager } from "./tools.js"; | ||||||
| import { FreeTextEditor } from "./freetext.js"; | import { FreeTextEditor } from "./freetext.js"; | ||||||
| import { InkEditor } from "./ink.js"; | import { InkEditor } from "./ink.js"; | ||||||
| import { PixelsPerInch } from "../display_utils.js"; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @typedef {Object} AnnotationEditorLayerOptions |  * @typedef {Object} AnnotationEditorLayerOptions | ||||||
| @ -492,14 +491,6 @@ class AnnotationEditorLayer { | |||||||
|   get scaleFactor() { |   get scaleFactor() { | ||||||
|     return this.viewport.scale; |     return this.viewport.scale; | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   /** |  | ||||||
|    * Get the zoom factor. |  | ||||||
|    * @returns {number} |  | ||||||
|    */ |  | ||||||
|   get zoomFactor() { |  | ||||||
|     return this.viewport.scale / PixelsPerInch.PDF_TO_CSS_UNITS; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export { AnnotationEditorLayer }; | export { AnnotationEditorLayer }; | ||||||
|  | |||||||
| @ -21,7 +21,6 @@ import { | |||||||
| } from "../../shared/util.js"; | } from "../../shared/util.js"; | ||||||
| import { AnnotationEditor } from "./editor.js"; | import { AnnotationEditor } from "./editor.js"; | ||||||
| import { bindEvents } from "./tools.js"; | import { bindEvents } from "./tools.js"; | ||||||
| import { PixelsPerInch } from "../display_utils.js"; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Basic text editor in order to create a FreeTex annotation. |  * Basic text editor in order to create a FreeTex annotation. | ||||||
| @ -94,9 +93,9 @@ class FreeTextEditor extends AnnotationEditor { | |||||||
|   getInitialTranslation() { |   getInitialTranslation() { | ||||||
|     // The start of the base line is where the user clicked.
 |     // The start of the base line is where the user clicked.
 | ||||||
|     return [ |     return [ | ||||||
|       -FreeTextEditor._internalPadding * this.parent.zoomFactor, |       -FreeTextEditor._internalPadding * this.parent.scaleFactor, | ||||||
|       -(FreeTextEditor._internalPadding + this.#fontSize) * |       -(FreeTextEditor._internalPadding + this.#fontSize) * | ||||||
|         this.parent.zoomFactor, |         this.parent.scaleFactor, | ||||||
|     ]; |     ]; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -217,7 +216,7 @@ class FreeTextEditor extends AnnotationEditor { | |||||||
|     this.editorDiv.contentEditable = true; |     this.editorDiv.contentEditable = true; | ||||||
| 
 | 
 | ||||||
|     const { style } = this.editorDiv; |     const { style } = this.editorDiv; | ||||||
|     style.fontSize = `calc(${this.#fontSize}px * var(--zoom-factor))`; |     style.fontSize = `${this.#fontSize}%`; | ||||||
|     style.color = this.#color; |     style.color = this.#color; | ||||||
| 
 | 
 | ||||||
|     this.div.append(this.editorDiv); |     this.div.append(this.editorDiv); | ||||||
| @ -244,7 +243,7 @@ class FreeTextEditor extends AnnotationEditor { | |||||||
|   /** @inheritdoc */ |   /** @inheritdoc */ | ||||||
|   serialize() { |   serialize() { | ||||||
|     const rect = this.editorDiv.getBoundingClientRect(); |     const rect = this.editorDiv.getBoundingClientRect(); | ||||||
|     const padding = FreeTextEditor._internalPadding * this.parent.zoomFactor; |     const padding = FreeTextEditor._internalPadding * this.parent.scaleFactor; | ||||||
|     const [x1, y1] = Util.applyTransform( |     const [x1, y1] = Util.applyTransform( | ||||||
|       [this.x + padding, this.y + padding + rect.height], |       [this.x + padding, this.y + padding + rect.height], | ||||||
|       this.parent.inverseViewportTransform |       this.parent.inverseViewportTransform | ||||||
| @ -257,7 +256,7 @@ class FreeTextEditor extends AnnotationEditor { | |||||||
|     return { |     return { | ||||||
|       annotationType: AnnotationEditorType.FREETEXT, |       annotationType: AnnotationEditorType.FREETEXT, | ||||||
|       color: [0, 0, 0], |       color: [0, 0, 0], | ||||||
|       fontSize: this.#fontSize / PixelsPerInch.PDF_TO_CSS_UNITS, |       fontSize: this.#fontSize, | ||||||
|       value: this.#content, |       value: this.#content, | ||||||
|       pageIndex: this.parent.pageIndex, |       pageIndex: this.parent.pageIndex, | ||||||
|       rect: [x1, y1, x2, y2], |       rect: [x1, y1, x2, y2], | ||||||
|  | |||||||
| @ -17,6 +17,13 @@ | |||||||
| 
 | 
 | ||||||
| .annotationLayer { | .annotationLayer { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|  |   font-size: calc(100px * var(--scale-factor)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .annotationLayer .buttonWidgetAnnotation.pushButton > img { | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  |   position: absolute; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .buttonWidgetAnnotation.checkBox input, | .annotationLayer .buttonWidgetAnnotation.checkBox input, | ||||||
|  | |||||||
| @ -215,7 +215,7 @@ class Rasterize { | |||||||
|       div.className = "annotationLayer"; |       div.className = "annotationLayer"; | ||||||
| 
 | 
 | ||||||
|       const [common, overrides] = await this.annotationStylePromise; |       const [common, overrides] = await this.annotationStylePromise; | ||||||
|       style.textContent = `${common}\n${overrides}`; |       style.textContent = `:root { --scale-factor: ${viewport.scale} } ${common}\n${overrides}`; | ||||||
| 
 | 
 | ||||||
|       const annotationViewport = viewport.clone({ dontFlip: true }); |       const annotationViewport = viewport.clone({ dontFlip: true }); | ||||||
|       const annotationImageMap = await convertCanvasesToImages( |       const annotationImageMap = await convertCanvasesToImages( | ||||||
| @ -234,6 +234,7 @@ class Rasterize { | |||||||
|         renderForms, |         renderForms, | ||||||
|         annotationCanvasMap: annotationImageMap, |         annotationCanvasMap: annotationImageMap, | ||||||
|       }; |       }; | ||||||
|  |       AnnotationLayer.setDimensions(div, annotationViewport); | ||||||
|       AnnotationLayer.render(parameters); |       AnnotationLayer.render(parameters); | ||||||
| 
 | 
 | ||||||
|       // Inline SVG images from text annotations.
 |       // Inline SVG images from text annotations.
 | ||||||
|  | |||||||
| @ -42,7 +42,7 @@ describe("Editor", () => { | |||||||
|           }); |           }); | ||||||
| 
 | 
 | ||||||
|           const data = "Hello PDF.js World !!"; |           const data = "Hello PDF.js World !!"; | ||||||
|           await page.mouse.click(rect.x + 10, rect.y + 10); |           await page.mouse.click(rect.x + 100, rect.y + 100); | ||||||
|           await page.type(`${editorPrefix}0 .internal`, data); |           await page.type(`${editorPrefix}0 .internal`, data); | ||||||
| 
 | 
 | ||||||
|           const editorRect = await page.$eval(`${editorPrefix}0`, el => { |           const editorRect = await page.$eval(`${editorPrefix}0`, el => { | ||||||
| @ -151,7 +151,7 @@ describe("Editor", () => { | |||||||
|           }); |           }); | ||||||
| 
 | 
 | ||||||
|           const data = "Hello PDF.js World !!"; |           const data = "Hello PDF.js World !!"; | ||||||
|           await page.mouse.click(rect.x + 10, rect.y + 10); |           await page.mouse.click(rect.x + 100, rect.y + 100); | ||||||
|           await page.type(`${editorPrefix}5 .internal`, data); |           await page.type(`${editorPrefix}5 .internal`, data); | ||||||
| 
 | 
 | ||||||
|           const editorRect = await page.$eval(`${editorPrefix}5`, el => { |           const editorRect = await page.$eval(`${editorPrefix}5`, el => { | ||||||
|  | |||||||
| @ -27,13 +27,14 @@ | |||||||
|   position: absolute; |   position: absolute; | ||||||
|   top: 0; |   top: 0; | ||||||
|   left: 0; |   left: 0; | ||||||
|  |   font-size: calc(100px * var(--scale-factor)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationEditorLayer .freeTextEditor { | .annotationEditorLayer .freeTextEditor { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   background: transparent; |   background: transparent; | ||||||
|   border-radius: 3px; |   border-radius: 3px; | ||||||
|   padding: calc(var(--freetext-padding) * var(--zoom-factor)); |   padding: calc(var(--freetext-padding) * var(--scale-factor)); | ||||||
|   resize: none; |   resize: none; | ||||||
|   width: auto; |   width: auto; | ||||||
|   height: auto; |   height: auto; | ||||||
|  | |||||||
| @ -27,9 +27,30 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | [data-annotation-rotation="90"] { | ||||||
|  |   transform: rotate(90deg) translateY(-100%); | ||||||
|  | } | ||||||
|  | [data-annotation-rotation="180"] { | ||||||
|  |   transform: rotate(180deg) translate(-100%, -100%); | ||||||
|  | } | ||||||
|  | [data-annotation-rotation="270"] { | ||||||
|  |   transform: rotate(270deg) translateX(-100%); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | .annotationLayer { | ||||||
|  |   position: absolute; | ||||||
|  |   top: 0; | ||||||
|  |   left: 0; | ||||||
|  |   font-size: calc(100px * var(--scale-factor)); | ||||||
|  |   pointer-events: none; | ||||||
|  |   transform-origin: 0 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .annotationLayer section { | .annotationLayer section { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   text-align: initial; |   text-align: initial; | ||||||
|  |   pointer-events: auto; | ||||||
|  |   box-sizing: border-box; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .linkAnnotation > a, | .annotationLayer .linkAnnotation > a, | ||||||
| @ -43,10 +64,8 @@ | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .buttonWidgetAnnotation.pushButton > canvas { | .annotationLayer .buttonWidgetAnnotation.pushButton > canvas { | ||||||
|   position: relative; |   width: 100%; | ||||||
|   top: 0; |   height: 100%; | ||||||
|   left: 0; |  | ||||||
|   z-index: -1; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .linkAnnotation > a:hover, | .annotationLayer .linkAnnotation > a:hover, | ||||||
| @ -59,6 +78,8 @@ | |||||||
| .annotationLayer .textAnnotation img { | .annotationLayer .textAnnotation img { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .textWidgetAnnotation input, | .annotationLayer .textWidgetAnnotation input, | ||||||
| @ -69,7 +90,7 @@ | |||||||
|   background-image: var(--annotation-unfocused-field-background); |   background-image: var(--annotation-unfocused-field-background); | ||||||
|   border: 1px solid transparent; |   border: 1px solid transparent; | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
|   font: 9px sans-serif; |   font: 9% sans-serif; | ||||||
|   height: 100%; |   height: 100%; | ||||||
|   margin: 0; |   margin: 0; | ||||||
|   vertical-align: top; |   vertical-align: top; | ||||||
| @ -186,27 +207,31 @@ | |||||||
| 
 | 
 | ||||||
| .annotationLayer .popupWrapper { | .annotationLayer .popupWrapper { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   width: 20em; |   font-size: calc(9px * var(--scale-factor)); | ||||||
|  |   width: 100%; | ||||||
|  |   min-width: calc(180px * var(--scale-factor)); | ||||||
|  |   pointer-events: none; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .popup { | .annotationLayer .popup { | ||||||
|   position: absolute; |   position: absolute; | ||||||
|   z-index: 200; |   z-index: 200; | ||||||
|   max-width: 20em; |   max-width: calc(180px * var(--scale-factor)); | ||||||
|   background-color: rgba(255, 255, 153, 1); |   background-color: rgba(255, 255, 153, 1); | ||||||
|   box-shadow: 0 2px 5px rgba(136, 136, 136, 1); |   box-shadow: 0 calc(2px * var(--scale-factor)) calc(5px * var(--scale-factor)) | ||||||
|   border-radius: 2px; |     rgba(136, 136, 136, 1); | ||||||
|   padding: 6px; |   border-radius: calc(2px * var(--scale-factor)); | ||||||
|   margin-left: 5px; |   padding: calc(6px * var(--scale-factor)); | ||||||
|  |   margin-left: calc(5px * var(--scale-factor)); | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
|   font: message-box; |   font: message-box; | ||||||
|   font-size: 9px; |  | ||||||
|   white-space: normal; |   white-space: normal; | ||||||
|   word-wrap: break-word; |   word-wrap: break-word; | ||||||
|  |   pointer-events: auto; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .popup > * { | .annotationLayer .popup > * { | ||||||
|   font-size: 9px; |   font-size: calc(9px * var(--scale-factor)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .popup h1 { | .annotationLayer .popup h1 { | ||||||
| @ -215,17 +240,18 @@ | |||||||
| 
 | 
 | ||||||
| .annotationLayer .popupDate { | .annotationLayer .popupDate { | ||||||
|   display: inline-block; |   display: inline-block; | ||||||
|   margin-left: 5px; |   margin-left: calc(5px * var(--scale-factor)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .popupContent { | .annotationLayer .popupContent { | ||||||
|   border-top: 1px solid rgba(51, 51, 51, 1); |   border-top: 1px solid rgba(51, 51, 51, 1); | ||||||
|   margin-top: 2px; |   margin-top: calc(2px * var(--scale-factor)); | ||||||
|   padding-top: 2px; |   padding-top: calc(2px * var(--scale-factor)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .richText > * { | .annotationLayer .richText > * { | ||||||
|   white-space: pre-wrap; |   white-space: pre-wrap; | ||||||
|  |   font-size: calc(9px * var(--scale-factor)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .annotationLayer .highlightAnnotation, | .annotationLayer .highlightAnnotation, | ||||||
| @ -244,3 +270,9 @@ | |||||||
| .annotationLayer .fileAttachmentAnnotation { | .annotationLayer .fileAttachmentAnnotation { | ||||||
|   cursor: pointer; |   cursor: pointer; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .annotationLayer section svg { | ||||||
|  |   position: absolute; | ||||||
|  |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|  | } | ||||||
|  | |||||||
| @ -117,11 +117,14 @@ class AnnotationLayerBuilder { | |||||||
|     if (this.div) { |     if (this.div) { | ||||||
|       // If an annotationLayer already exists, refresh its children's
 |       // If an annotationLayer already exists, refresh its children's
 | ||||||
|       // transformation matrices.
 |       // transformation matrices.
 | ||||||
|  |       AnnotationLayer.setDimensions(this.div, viewport); | ||||||
|       AnnotationLayer.update(parameters); |       AnnotationLayer.update(parameters); | ||||||
|     } else { |     } else { | ||||||
|       // Create an annotation layer div and render the annotations
 |       // Create an annotation layer div and render the annotations
 | ||||||
|       // if there is at least one annotation.
 |       // if there is at least one annotation.
 | ||||||
|       this.div = document.createElement("div"); |       this.div = document.createElement("div"); | ||||||
|  |       AnnotationLayer.setDimensions(this.div, viewport); | ||||||
|  | 
 | ||||||
|       this.div.className = "annotationLayer"; |       this.div.className = "annotationLayer"; | ||||||
|       this.pageDiv.append(this.div); |       this.pageDiv.append(this.div); | ||||||
|       parameters.div = this.div; |       parameters.div = this.div; | ||||||
|  | |||||||
| @ -1048,7 +1048,10 @@ class BaseViewer { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     docStyle.setProperty("--zoom-factor", newScale); |     docStyle.setProperty( | ||||||
|  |       "--scale-factor", | ||||||
|  |       newScale * PixelsPerInch.PDF_TO_CSS_UNITS | ||||||
|  |     ); | ||||||
| 
 | 
 | ||||||
|     const updateArgs = { scale: newScale }; |     const updateArgs = { scale: newScale }; | ||||||
|     for (const pageView of this._pages) { |     for (const pageView of this._pages) { | ||||||
|  | |||||||
| @ -377,7 +377,7 @@ class PDFPageView { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     if (this._isStandalone) { |     if (this._isStandalone) { | ||||||
|       docStyle.setProperty("--zoom-factor", this.scale); |       docStyle.setProperty("--scale-factor", this.viewport.scale); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (this.svg) { |     if (this.svg) { | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ | |||||||
|   --page-margin: 1px auto -8px; |   --page-margin: 1px auto -8px; | ||||||
|   --page-border: 9px solid transparent; |   --page-border: 9px solid transparent; | ||||||
|   --spreadHorizontalWrapped-margin-LR: -3.5px; |   --spreadHorizontalWrapped-margin-LR: -3.5px; | ||||||
|   --zoom-factor: 1; |   --scale-factor: 1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @media screen and (forced-colors: active) { | @media screen and (forced-colors: active) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user