Merge pull request 'azeem' (#75) from azeem into dev
Reviewed-on: https://gitea.maskantech.in/gitea_admin/pdf/pulls/75
This commit is contained in:
@@ -341,6 +341,7 @@ PYBIND11_MODULE(pdfengine, m) {
|
||||
.def_readonly("content", &pdfengine::PdfPage::AnnotationInfo::content)
|
||||
.def_readonly("timestamp", &pdfengine::PdfPage::AnnotationInfo::timestamp)
|
||||
.def_readonly("page_index", &pdfengine::PdfPage::AnnotationInfo::pageIndex)
|
||||
.def_readonly("thickness", &pdfengine::PdfPage::AnnotationInfo::thickness)
|
||||
.def_readonly("paths", &pdfengine::PdfPage::AnnotationInfo::paths)
|
||||
.def_readonly("field_name", &pdfengine::PdfPage::AnnotationInfo::fieldName)
|
||||
.def_readonly("field_value", &pdfengine::PdfPage::AnnotationInfo::fieldValue)
|
||||
|
||||
@@ -198,6 +198,7 @@ public:
|
||||
std::string content;
|
||||
std::string timestamp;
|
||||
int pageIndex = 0;
|
||||
double thickness = 0.0;
|
||||
std::vector<std::vector<Point2D>> paths;
|
||||
std::vector<std::array<Point2D, 4>> quadPoints;
|
||||
|
||||
|
||||
@@ -129,6 +129,7 @@ std::expected<void, EngineError> PdfiumDocument::applyOp_decoration(const nlohma
|
||||
int subtype = FPDF_ANNOT_UNDERLINE;
|
||||
if (type == "strikeout") subtype = FPDF_ANNOT_STRIKEOUT;
|
||||
else if (type == "squiggly") subtype = FPDF_ANNOT_SQUIGGLY;
|
||||
else if (type == "highlight") subtype = FPDF_ANNOT_HIGHLIGHT;
|
||||
|
||||
FPDF_ANNOTATION annot = FPDFPage_CreateAnnot(page, subtype);
|
||||
if (!annot) {
|
||||
@@ -383,6 +384,10 @@ std::expected<void, EngineError> PdfiumDocument::applyOp_freehand(const nlohmann
|
||||
float thickness = static_cast<float>(data.value("thickness", 2.0));
|
||||
FPDFAnnot_SetBorder(annot, 0.0f, 0.0f, thickness);
|
||||
|
||||
std::string tStr = std::to_string(thickness);
|
||||
auto tUtf16 = utf8_to_utf16le(tStr);
|
||||
FPDFAnnot_SetStringValue(annot, "CustomThickness", reinterpret_cast<FPDF_WIDESTRING>(tUtf16.data()));
|
||||
|
||||
float minX = 1e9f, minY = 1e9f, maxX = -1e9f, maxY = -1e9f;
|
||||
bool anyPoints = false;
|
||||
|
||||
@@ -549,6 +554,10 @@ std::expected<void, EngineError> PdfiumDocument::applyOp_updateAnnotation(const
|
||||
if (data.contains("thickness")) {
|
||||
float thickness = static_cast<float>(data["thickness"].get<double>());
|
||||
FPDFAnnot_SetBorder(targetAnnot, 0.0f, 0.0f, thickness);
|
||||
|
||||
std::string tStr = std::to_string(thickness);
|
||||
auto tUtf16 = utf8_to_utf16le(tStr);
|
||||
FPDFAnnot_SetStringValue(targetAnnot, "CustomThickness", reinterpret_cast<FPDF_WIDESTRING>(tUtf16.data()));
|
||||
}
|
||||
|
||||
if (data.contains("text")) {
|
||||
|
||||
@@ -600,6 +600,21 @@ std::expected<std::vector<PdfPage::AnnotationInfo>, EngineError> PdfiumPage::ext
|
||||
}
|
||||
|
||||
if (subtype == FPDF_ANNOT_INK) {
|
||||
float h_radius = 0, v_radius = 0, border_width = 1.0f;
|
||||
info.thickness = 2.0; // default
|
||||
|
||||
unsigned long ctLen = FPDFAnnot_GetStringValue(annot, "CustomThickness", nullptr, 0);
|
||||
if (ctLen > 2) {
|
||||
std::vector<char16_t> ctBuf(ctLen / 2);
|
||||
FPDFAnnot_GetStringValue(annot, "CustomThickness", reinterpret_cast<FPDF_WCHAR*>(ctBuf.data()), ctLen);
|
||||
std::string ctStr = utf16le_to_utf8(ctBuf.data(), ctBuf.size());
|
||||
try {
|
||||
info.thickness = std::stod(ctStr);
|
||||
} catch (...) {}
|
||||
} else if (FPDFAnnot_GetBorder(annot, &h_radius, &v_radius, &border_width)) {
|
||||
info.thickness = border_width;
|
||||
}
|
||||
|
||||
const double pageH = height();
|
||||
unsigned long strokeCount = FPDFAnnot_GetInkListCount(annot);
|
||||
for (unsigned long s = 0; s < strokeCount; ++s) {
|
||||
|
||||
@@ -270,7 +270,7 @@ function App() {
|
||||
if (!can('canAnnotate')) { denyToast('Text decorations'); return; }
|
||||
const quadPointsBackend = lines.map((line) => {
|
||||
const lx = line.x / zoom, ly = line.y / zoom, lw = line.width / zoom, lh = line.height / zoom;
|
||||
return { x1: lx, y1: ly + lh, x2: lx + lw, y2: ly + lh, x3: lx + lw, y3: ly, x4: lx, y4: ly };
|
||||
return { x1: lx, y1: ly, x2: lx + lw, y2: ly, x3: lx, y3: ly + lh, x4: lx + lw, y4: ly + lh };
|
||||
});
|
||||
|
||||
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
||||
@@ -280,7 +280,7 @@ function App() {
|
||||
if (ly < minY) minY = ly;
|
||||
if (lx + lw > maxX) maxX = lx + lw;
|
||||
if (ly + lh > maxY) maxY = ly + lh;
|
||||
return [{ x: lx, y: ly + lh }, { x: lx + lw, y: ly + lh }, { x: lx + lw, y: ly }, { x: lx, y: ly }];
|
||||
return [{ x: lx, y: ly }, { x: lx + lw, y: ly }, { x: lx, y: ly + lh }, { x: lx + lw, y: ly + lh }];
|
||||
});
|
||||
|
||||
const newAnno = {
|
||||
|
||||
@@ -208,7 +208,7 @@ const NotesTab: React.FC<{ annotations: Annotation[]; onNavigate: (a: Annotation
|
||||
<span className="text-[10px] font-medium text-[#98a1ad]">{a.author}</span>
|
||||
</CustomButton>
|
||||
|
||||
{onUpdate && (a.type === 'highlight' || a.type === 'ink' || a.type === 'comment') && (
|
||||
{onUpdate && (['highlight', 'ink', 'comment', 'strikeout', 'underline', 'squiggly'].includes(a.type)) && (
|
||||
<div className="flex items-center gap-2 mt-1 px-1" onClick={e => e.stopPropagation()}>
|
||||
<input
|
||||
type="color"
|
||||
|
||||
@@ -194,8 +194,10 @@ export class TextSelectionModel {
|
||||
}
|
||||
const rects: SelRect[] = [];
|
||||
for (const arr of byLine.values()) {
|
||||
const x = Math.min(...arr.map((g) => g.x));
|
||||
const right = Math.max(...arr.map((g) => g.right));
|
||||
const nonSpace = arr.filter(g => !/^\s+$/.test(g.text));
|
||||
const measureArr = nonSpace.length > 0 ? nonSpace : arr;
|
||||
const x = Math.min(...measureArr.map((g) => g.x));
|
||||
const right = Math.max(...measureArr.map((g) => g.right));
|
||||
const band = this.lines[arr[0].line];
|
||||
rects.push({ x, y: band.top, w: right - x, h: band.bottom - band.top });
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ export const AnnotationLayer: React.FC<AnnotationLayerProps> = ({
|
||||
onPointerDown={(e) => handlePointerDown(e, anno)}
|
||||
onPointerMove={(e) => handlePointerMove(e, anno.id)}
|
||||
onPointerUp={(e) => handlePointerUp(e, anno)}
|
||||
className={`absolute ${isDraggable(anno.type) ? 'cursor-move' : 'cursor-pointer'} rounded-[2px] transition-[opacity,box-shadow] duration-150 hover:shadow-[0_2px_8px_rgba(16,24,40,0.18)] type-${anno.type} ${isDragging ? 'shadow-lg z-50' : ''}`}
|
||||
className={`absolute ${isDraggable(anno.type) ? 'cursor-move' : 'cursor-pointer'} rounded-[2px] transition-[opacity,box-shadow] duration-150 ${['highlight', 'strikeout', 'underline', 'squiggly'].includes(anno.type) ? '' : 'hover:shadow-[0_2px_8px_rgba(16,24,40,0.18)]'} type-${anno.type} ${isDragging ? 'shadow-lg z-50' : ''}`}
|
||||
style={{
|
||||
left: `${scaledBbox.x + currentOffset.x * zoom}px`,
|
||||
top: `${scaledBbox.y + currentOffset.y * zoom}px`,
|
||||
|
||||
@@ -111,6 +111,7 @@ def get_document_annotations(document_id: str) -> list[AnnotationResponse]:
|
||||
content=a.content,
|
||||
timestamp=getattr(a, "timestamp", None),
|
||||
pageIndex=a.page_index,
|
||||
thickness=getattr(a, "thickness", None),
|
||||
paths=[[{"x": p.x, "y": p.y} for p in stroke] for stroke in getattr(a, "paths", [])],
|
||||
quadPoints=getattr(a, "quad_points", []),
|
||||
fieldName=getattr(a, "field_name", None),
|
||||
|
||||
@@ -283,6 +283,20 @@ class SquigglyOperation(BaseModel):
|
||||
pageIndex: int = Field(..., ge=0)
|
||||
data: DecorationData
|
||||
|
||||
class SignatureData(BaseModel):
|
||||
x: float
|
||||
y: float
|
||||
width: float
|
||||
height: float
|
||||
image_data: str | None = None
|
||||
author: str = "Signer"
|
||||
|
||||
class SignatureOperation(BaseModel):
|
||||
id: str
|
||||
type: Literal["signature"]
|
||||
pageIndex: int = Field(..., ge=0)
|
||||
data: SignatureData
|
||||
|
||||
EditOperation = Annotated[
|
||||
TextOverlayOperation
|
||||
| RedactionOperation
|
||||
@@ -301,7 +315,9 @@ EditOperation = Annotated[
|
||||
| ReflowParagraphOperation
|
||||
| UnderlineOperation
|
||||
| StrikeoutOperation
|
||||
| SquigglyOperation,
|
||||
| SquigglyOperation
|
||||
| SignatureOperation
|
||||
,
|
||||
Field(discriminator="type"),
|
||||
]
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ class AnnotationResponse(BaseModel):
|
||||
content: str
|
||||
timestamp: str | None = None
|
||||
pageIndex: int
|
||||
thickness: float | None = None
|
||||
paths: list[list[dict[str, float]]] = []
|
||||
quadPoints: list[list[dict[str, float]]] = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user