141 lines
4.1 KiB
TypeScript
141 lines
4.1 KiB
TypeScript
import * as fabric from 'fabric';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { useEditorStore } from '../store';
|
|
import type { ToolHandler } from '../../../lib/annotations/registry';
|
|
import type { Annotation, TextAnnotation } from '../../../lib/annotations/types';
|
|
import { screenToPdf, pdfRectToScreen } from '../../../lib/coords';
|
|
import type { ViewportParams } from '../../../lib/coords';
|
|
|
|
export const TextTool: ToolHandler = {
|
|
name: 'text',
|
|
|
|
onActivate: (canvas: fabric.Canvas) => {
|
|
canvas.defaultCursor = 'text';
|
|
canvas.selection = false;
|
|
},
|
|
|
|
onDeactivate: (canvas: fabric.Canvas) => {
|
|
canvas.defaultCursor = 'default';
|
|
},
|
|
|
|
onPointerDown: (e: any, canvas: fabric.Canvas, vp: ViewportParams, pageNumber: number) => {
|
|
// If we clicked on an existing object, don't create a new one
|
|
if (e.target) return;
|
|
|
|
// Create the Fabric object first so the user can immediately type
|
|
const pointer = e.scenePoint || canvas.getScenePoint(e.e);
|
|
const textbox = new fabric.Textbox('', {
|
|
left: pointer.x,
|
|
top: pointer.y,
|
|
width: 150 * vp.scale,
|
|
fontFamily: 'Liberation Sans',
|
|
fontSize: 14 * vp.scale * (vp.dpr || 1), // scaled loosely for screen display
|
|
fontWeight: 'normal',
|
|
fontStyle: 'normal',
|
|
fill: '#000000',
|
|
backgroundColor: undefined,
|
|
originX: 'left',
|
|
originY: 'top',
|
|
transparentCorners: false,
|
|
cornerColor: '#3b82f6',
|
|
cornerStrokeColor: '#3b82f6',
|
|
borderColor: '#3b82f6',
|
|
cornerSize: 8,
|
|
padding: 5,
|
|
});
|
|
|
|
// Hide all corners and vertical resizers, leaving only middle-left and middle-right for width
|
|
textbox.setControlsVisibility({
|
|
tl: false,
|
|
tr: false,
|
|
bl: false,
|
|
br: false,
|
|
mt: false,
|
|
mb: false,
|
|
mtr: false // hide rotation handle for now
|
|
});
|
|
|
|
canvas.add(textbox);
|
|
canvas.setActiveObject(textbox);
|
|
textbox.enterEditing();
|
|
|
|
textbox.on('editing:exited', () => {
|
|
if (!textbox.text || textbox.text.trim() === '') {
|
|
canvas.remove(textbox);
|
|
return;
|
|
}
|
|
|
|
const pt = screenToPdf({ x: textbox.left!, y: textbox.top! }, vp);
|
|
|
|
const newAnn: TextAnnotation = {
|
|
id: uuidv4(),
|
|
page: pageNumber,
|
|
type: 'text',
|
|
rect: {
|
|
x: pt.x,
|
|
y: pt.y,
|
|
width: textbox.width! / (vp.scale * (vp.dpr || 1)),
|
|
height: textbox.height! / (vp.scale * (vp.dpr || 1))
|
|
},
|
|
rotation: 0,
|
|
z: 0,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
props: {
|
|
text: textbox.text,
|
|
fontFamily: 'Liberation Sans',
|
|
fontSize: 14,
|
|
color: '#000000',
|
|
align: 'left',
|
|
bold: false,
|
|
italic: false,
|
|
lineHeight: 1.2,
|
|
highlightColor: null
|
|
}
|
|
};
|
|
|
|
// We attach the ID to the fabric object so we can link it
|
|
(textbox as any).id = newAnn.id;
|
|
|
|
useEditorStore.getState().addAnnotation(newAnn);
|
|
});
|
|
},
|
|
|
|
renderToFabric: (annotation: Annotation, canvas: fabric.Canvas, vp: ViewportParams) => {
|
|
const textAnn = annotation as TextAnnotation;
|
|
const pt = pdfRectToScreen(textAnn.rect, vp);
|
|
|
|
const textbox = new fabric.Textbox(textAnn.props.text, {
|
|
left: pt.x,
|
|
top: pt.y,
|
|
width: textAnn.rect.width * vp.scale * (vp.dpr || 1),
|
|
fontFamily: textAnn.props.fontFamily,
|
|
fontSize: textAnn.props.fontSize * vp.scale * (vp.dpr || 1),
|
|
fontWeight: textAnn.props.bold ? 'bold' : 'normal',
|
|
fontStyle: textAnn.props.italic ? 'italic' : 'normal',
|
|
fill: textAnn.props.color,
|
|
backgroundColor: textAnn.props.highlightColor || undefined,
|
|
originX: 'left',
|
|
originY: 'top',
|
|
transparentCorners: false,
|
|
cornerColor: '#3b82f6',
|
|
cornerStrokeColor: '#3b82f6',
|
|
borderColor: '#3b82f6',
|
|
cornerSize: 8,
|
|
padding: 5,
|
|
});
|
|
|
|
textbox.setControlsVisibility({
|
|
tl: false,
|
|
tr: false,
|
|
bl: false,
|
|
br: false,
|
|
mt: false,
|
|
mb: false,
|
|
mtr: false
|
|
});
|
|
|
|
(textbox as any).id = annotation.id;
|
|
canvas.add(textbox);
|
|
}
|
|
};
|