Paperjet/backend/app/schemas/annotations.py

136 lines
2.9 KiB
Python

from datetime import datetime
from typing import Any, Literal
from uuid import UUID
from pydantic import BaseModel, Field
class Rect(BaseModel):
x: float
y: float
width: float
height: float
class AnnotationBase(BaseModel):
id: UUID
page: int = Field(ge=0)
type: str
rect: Rect
rotation: float = 0.0
z: int = 0
createdAt: datetime
updatedAt: datetime
class TextProps(BaseModel):
text: str
fontFamily: str = "Liberation Sans"
fontSize: float = 14
color: str = "#000000"
align: Literal["left", "center", "right"] = "left"
bold: bool = False
italic: bool = False
lineHeight: float = 1.2
highlightColor: str | None = None
styles: dict[str, Any] | None = None
class TextAnnotation(AnnotationBase):
type: Literal["text"]
props: TextProps
class DrawProps(BaseModel):
paths: list[tuple[float, float]] = Field(default_factory=list)
svgPath: str | None = None
strokeColor: str = "#000000"
strokeWidth: float = 2
opacity: float = 1.0
class DrawAnnotation(AnnotationBase):
type: Literal["draw"]
props: DrawProps
class SignatureDrawProps(BaseModel):
mode: Literal["draw"]
ref: str
strokeColor: str = "#000000"
class SignatureTypeProps(BaseModel):
mode: Literal["type"]
text: str
fontFamily: str
color: str = "#000000"
class SignatureAnnotation(AnnotationBase):
type: Literal["signature"]
props: SignatureDrawProps | SignatureTypeProps = Field(discriminator="mode")
class ImageProps(BaseModel):
ref: str
naturalWidth: float
naturalHeight: float
class ImageAnnotation(AnnotationBase):
type: Literal["image"]
props: ImageProps
class HighlightProps(BaseModel):
color: str = "#FFEB3B"
opacity: float = 0.3
class HighlightAnnotation(AnnotationBase):
type: Literal["highlight"]
props: HighlightProps
class ShapeProps(BaseModel):
kind: Literal["rect", "ellipse", "line", "arrow"]
strokeColor: str = "#000000"
fillColor: str = "transparent"
strokeWidth: float = 2
start: tuple[float, float] | None = None
end: tuple[float, float] | None = None
class ShapeAnnotation(AnnotationBase):
type: Literal["shape"]
props: ShapeProps
KnownAnnotation = (
TextAnnotation
| DrawAnnotation
| SignatureAnnotation
| ImageAnnotation
| HighlightAnnotation
| ShapeAnnotation
)
# Working state is intentionally opaque. Keeping this payload as dictionaries
# lets newer clients round-trip annotation types that this server cannot export
# yet; the export registry skips those types with a warning.
AnnotationPayload = dict[str, Any]
class AnnotationStateResponse(BaseModel):
data: list[AnnotationPayload]
updatedAt: datetime
class AnnotationStateUpdateRequest(BaseModel):
data: list[AnnotationPayload]
baseUpdatedAt: datetime | None = None
class AnnotationStateUpdateResponse(BaseModel):
updatedAt: datetime