fix: file display, persistence, PDF support and save error handling
- Add file proxy route in maker blueprint (X-API-Key auth for browser requests) - Persist file_path/annotations_json to DB via RecipeCreate/RecipeUpdate schemas - Fix canvas sizing using grandparent container instead of Fabric.js wrapper div - Defer canvas init with requestAnimationFrame for x-show timing - Add PDF.js support in annotation-editor and annotation-viewer - Fix annotations_json double-serialization (parse string to object before send) - Handle FastAPI 422 validation error arrays in api_client and JS error display - Update template URLs to use /maker/api/files/ proxy path Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -80,47 +80,90 @@ function annotationViewer() {
|
||||
},
|
||||
|
||||
/**
|
||||
* Carica e renderizza l'immagine con scaling
|
||||
* Carica e renderizza l'immagine (o la prima pagina PDF) con scaling
|
||||
*/
|
||||
loadImage() {
|
||||
var isPdf = this.imageUrl.toLowerCase().replace(/\?.*$/, '').endsWith('.pdf');
|
||||
|
||||
if (isPdf && typeof pdfjsLib !== 'undefined') {
|
||||
this._loadPdf();
|
||||
} else {
|
||||
this._loadRasterImage();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render first page of a PDF via PDF.js
|
||||
*/
|
||||
_loadPdf() {
|
||||
const self = this;
|
||||
pdfjsLib.getDocument(this.imageUrl).promise.then(function (pdf) {
|
||||
return pdf.getPage(1);
|
||||
}).then(function (page) {
|
||||
const pdfScale = 2; // render at 2x for clarity
|
||||
const viewport = page.getViewport({ scale: pdfScale });
|
||||
|
||||
const offCanvas = document.createElement('canvas');
|
||||
offCanvas.width = viewport.width;
|
||||
offCanvas.height = viewport.height;
|
||||
const offCtx = offCanvas.getContext('2d');
|
||||
|
||||
page.render({ canvasContext: offCtx, viewport: viewport }).promise.then(function () {
|
||||
// Use rendered PDF page as if it were an image
|
||||
self._drawImageOnCanvas(offCanvas, viewport.width, viewport.height);
|
||||
});
|
||||
}).catch(function (err) {
|
||||
console.error('AnnotationViewer: failed to load PDF:', err);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a raster image (PNG, JPG, etc.)
|
||||
*/
|
||||
_loadRasterImage() {
|
||||
const self = this;
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
// Calculate scale to fit container
|
||||
const container = this.canvas.parentElement;
|
||||
if (!container) {
|
||||
console.error('AnnotationViewer: parent container not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight || 500; // fallback height
|
||||
|
||||
this.scale = Math.min(
|
||||
containerWidth / img.width,
|
||||
containerHeight / img.height,
|
||||
1 // non ingrandire oltre dimensione originale
|
||||
);
|
||||
|
||||
this.canvas.width = img.width * this.scale;
|
||||
this.canvas.height = img.height * this.scale;
|
||||
|
||||
// Draw image
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height);
|
||||
this.imageLoaded = true;
|
||||
|
||||
// Draw annotations on top
|
||||
this.drawAnnotations();
|
||||
img.onload = function () {
|
||||
self._drawImageOnCanvas(img, img.width, img.height);
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
console.error('AnnotationViewer: failed to load image:', this.imageUrl);
|
||||
img.onerror = function () {
|
||||
console.error('AnnotationViewer: failed to load image:', self.imageUrl);
|
||||
};
|
||||
|
||||
img.src = this.imageUrl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Draw an image source (Image or Canvas) scaled to fit the container
|
||||
*/
|
||||
_drawImageOnCanvas(source, srcWidth, srcHeight) {
|
||||
const container = this.canvas.parentElement;
|
||||
if (!container) {
|
||||
console.error('AnnotationViewer: parent container not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const containerWidth = container.clientWidth;
|
||||
const containerHeight = container.clientHeight || 500;
|
||||
|
||||
this.scale = Math.min(
|
||||
containerWidth / srcWidth,
|
||||
containerHeight / srcHeight,
|
||||
1
|
||||
);
|
||||
|
||||
this.canvas.width = srcWidth * this.scale;
|
||||
this.canvas.height = srcHeight * this.scale;
|
||||
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.ctx.drawImage(source, 0, 0, this.canvas.width, this.canvas.height);
|
||||
this.imageLoaded = true;
|
||||
|
||||
this.drawAnnotations();
|
||||
},
|
||||
|
||||
/**
|
||||
* Disegna tutti i markers sulle annotazioni
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user