* Create an image annotation from a File/Blob at the given screen position. * If no position is given, places the image at the center of the current page.
( file: File | Blob, screenX?: number, screenY?: number, )
| 5682 | * If no position is given, places the image at the center of the current page. |
| 5683 | */ |
| 5684 | function addImageFromFile( |
| 5685 | file: File | Blob, |
| 5686 | screenX?: number, |
| 5687 | screenY?: number, |
| 5688 | ): void { |
| 5689 | const reader = new FileReader(); |
| 5690 | reader.onload = () => { |
| 5691 | const dataUrl = reader.result as string; |
| 5692 | const base64 = dataUrl.split(",")[1]; |
| 5693 | const mimeType = |
| 5694 | file.type || (base64.startsWith("/9j/") ? "image/jpeg" : "image/png"); |
| 5695 | |
| 5696 | const img = new Image(); |
| 5697 | img.onload = () => { |
| 5698 | const maxWidth = 200; // PDF points |
| 5699 | const aspectRatio = img.naturalHeight / img.naturalWidth; |
| 5700 | const width = Math.min(img.naturalWidth, maxWidth); |
| 5701 | const height = width * aspectRatio; |
| 5702 | |
| 5703 | // Convert screen position to PDF internal coords, or default to page center |
| 5704 | let pdfX: number; |
| 5705 | let pdfInternalY: number; |
| 5706 | if (screenX != null && screenY != null) { |
| 5707 | pdfX = screenX / scale; |
| 5708 | pdfInternalY = (containerHtmlEl.clientHeight - screenY) / scale; |
| 5709 | } else { |
| 5710 | // Center on the visible page area |
| 5711 | const pageW = containerHtmlEl.clientWidth / scale; |
| 5712 | const pageH = containerHtmlEl.clientHeight / scale; |
| 5713 | pdfX = pageW / 2 - width / 2; |
| 5714 | pdfInternalY = pageH / 2 + height / 2; |
| 5715 | } |
| 5716 | |
| 5717 | const id = `img_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`; |
| 5718 | const def: ImageAnnotation = { |
| 5719 | type: "image", |
| 5720 | id, |
| 5721 | page: currentPage, |
| 5722 | x: pdfX, |
| 5723 | y: pdfInternalY, |
| 5724 | width, |
| 5725 | height, |
| 5726 | imageData: base64, |
| 5727 | mimeType, |
| 5728 | }; |
| 5729 | |
| 5730 | // Downscale if base64 data is too large (> ~300KB) |
| 5731 | if (base64.length > 400_000) { |
| 5732 | const canvas = document.createElement("canvas"); |
| 5733 | const maxDim = 800; |
| 5734 | let w = img.naturalWidth; |
| 5735 | let h = img.naturalHeight; |
| 5736 | if (w > maxDim || h > maxDim) { |
| 5737 | const ratio = Math.min(maxDim / w, maxDim / h); |
| 5738 | w = Math.round(w * ratio); |
| 5739 | h = Math.round(h * ratio); |
| 5740 | } |
| 5741 | canvas.width = w; |
no test coverage detected
searching dependent graphs…