Skip to content

Commit

Permalink
Implement border for sinaturepad question (#358)
Browse files Browse the repository at this point in the history
  • Loading branch information
dk981234 authored Dec 4, 2024
1 parent 31f5686 commit a891f9f
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 25 deletions.
36 changes: 30 additions & 6 deletions src/flat_layout/flat_signaturepad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { IQuestion, QuestionSignaturePadModel } from 'survey-core';
import { SurveyPDF } from '../survey';
import { FlatQuestion } from './flat_question';
import { FlatRepository } from './flat_repository';
import { IPoint, DocController } from '../doc_controller';
import { IPdfBrick } from '../pdf_render/pdf_brick';
import { SurveyHelper } from '../helper_survey';
import { IPoint, DocController, IRect, ISize } from '../doc_controller';
import { IPdfBrick, PdfBrick } from '../pdf_render/pdf_brick';
import { IBorderDescription, SurveyHelper } from '../helper_survey';
import { EmptyBrick } from '../pdf_render/pdf_empty';
import { CompositeBrick } from '../pdf_render/pdf_composite';

export class FlatSignaturePad extends FlatQuestion {
protected question: QuestionSignaturePadModel;
public static BORDER_STYLE: 'dashed' | 'solid' | 'none' = 'dashed';
public constructor(protected survey: SurveyPDF,
question: IQuestion, controller: DocController) {
super(survey, question, controller);
Expand All @@ -25,15 +26,38 @@ export class FlatSignaturePad extends FlatQuestion {
public async generateSign(point: IPoint): Promise<IPdfBrick> {
const width = SurveyHelper.pxToPt(<any>this.question.signatureWidth);
const height = SurveyHelper.pxToPt(<any>this.question.signatureHeight);
let brick: PdfBrick;
if(this.question.value) {
return await SurveyHelper.createImageFlat(point,
brick = await SurveyHelper.createImageFlat(point,
this.question, this.controller, { link: this.getSignImageUrl(),
width: width,
height: height }, false
);
) as PdfBrick;
} else {
return new EmptyBrick(SurveyHelper.createRect(point, width, height));
brick = new EmptyBrick(SurveyHelper.createRect(point, width, height));
}
if(FlatSignaturePad.BORDER_STYLE !== 'none') {
brick.afterRenderCallback = () => {
const borderOptions: IBorderDescription = {
height: brick.width,
width: brick.width,
yTop: brick.yTop,
yBot: brick.yBot,
xLeft: brick.xLeft,
xRight: brick.xRight,
formBorderColor: brick.formBorderColor,
rounded: false,
outside: true,
dashStyle: FlatSignaturePad.BORDER_STYLE == 'dashed' ? {
dashArray: [5],
dashPhase: 0
} : undefined
};
SurveyHelper.renderFlatBorders(this.controller, borderOptions);
};
}

return brick;
}

public async generateFlatsContent(point: IPoint): Promise<IPdfBrick[]> {
Expand Down
58 changes: 40 additions & 18 deletions src/helper_survey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { RowlineBrick } from './pdf_render/pdf_rowline';
import { CompositeBrick } from './pdf_render/pdf_composite';
import { AdornersOptions } from './event_handler/adorners';

export type IBorderDescription = IRect & ISize & Pick<PdfBrick, 'formBorderColor'> & { rounded?: boolean, dashStyle?: { dashArray: [number, number] | [number], dashPhase: number }, outside?: boolean };

export class SurveyHelper {
public static EPSILON: number = 2.2204460492503130808472633361816e-15;
public static TITLE_SURVEY_FONT_SIZE_SCALE: number = 1.7;
Expand Down Expand Up @@ -575,23 +577,43 @@ export class SurveyHelper {
controller.popMargins();
return textFlat;
}

public static renderFlatBorders(controller: DocController, flat: IRect & ISize & Pick<PdfBrick, 'formBorderColor'>): void {
public static renderFlatBorders(controller: DocController, borderOptions: IBorderDescription): void {
if (!this.FORM_BORDER_VISIBLE) return;
const minSide: number = Math.min(flat.width, flat.height);
borderOptions.rounded = borderOptions.rounded ?? true;
borderOptions.outside = borderOptions.outside ?? false;
const minSide: number = Math.min(borderOptions.width, borderOptions.height);
const borderWidth = this.getBorderWidth(controller);
const visibleWidth: number = controller.unitHeight * this.VISIBLE_BORDER_SCALE * this.BORDER_SCALE;
const visibleScale: number = this.formScale(controller, flat) + visibleWidth / minSide;
const unvisibleWidth: number = controller.unitHeight * this.UNVISIBLE_BORDER_SCALE * this.BORDER_SCALE;
const unvisibleScale: number = 1.0 - unvisibleWidth / minSide;
const unvisibleRadius: number = this.RADIUS_SCALE * unvisibleWidth;
const visibleScale: number = borderOptions.outside ? (minSide + borderWidth) / minSide - visibleWidth / minSide : (minSide - borderWidth) / minSide + visibleWidth / minSide;
const oldDrawColor: string = controller.doc.getDrawColor();
controller.doc.setDrawColor(flat.formBorderColor);
controller.doc.setDrawColor(borderOptions.formBorderColor);
controller.doc.setLineWidth(visibleWidth);
controller.doc.rect(...this.createAcroformRect(this.scaleRect(flat, visibleScale)));
controller.doc.setDrawColor(this.BACKGROUND_COLOR);
controller.doc.setLineWidth(unvisibleWidth);
controller.doc.roundedRect(...this.createAcroformRect(
this.scaleRect(flat, unvisibleScale)), unvisibleRadius, unvisibleRadius);
const scaledRect = this.scaleRect(borderOptions, visibleScale);
if(borderOptions.dashStyle) {
const dashStyle = borderOptions.dashStyle;
const borderLength = (Math.abs(scaledRect.yTop - scaledRect.yBot) + Math.abs(scaledRect.xLeft - scaledRect.xRight)) * 2;
const dashWithSpaceSize = dashStyle.dashArray[0] + (dashStyle.dashArray[1] ?? dashStyle.dashArray[0]);
const dashSize = dashStyle.dashArray[0] + (borderLength % dashWithSpaceSize) / Math.floor(borderLength / dashWithSpaceSize);

controller.doc.setLineDashPattern(
[dashSize, dashStyle.dashArray[1] ?? dashStyle.dashArray[0]],
dashStyle.dashPhase
);
}

controller.doc.rect(...this.createAcroformRect(scaledRect));
if(borderOptions.rounded) {
const unvisibleWidth: number = controller.unitHeight * this.UNVISIBLE_BORDER_SCALE * this.BORDER_SCALE;
const unvisibleScale: number = 1.0 - unvisibleWidth / minSide;
const unvisibleRadius: number = this.RADIUS_SCALE * unvisibleWidth;
controller.doc.setDrawColor(this.BACKGROUND_COLOR);
controller.doc.setLineWidth(unvisibleWidth);
controller.doc.roundedRect(...this.createAcroformRect(
this.scaleRect(borderOptions, unvisibleScale)), unvisibleRadius, unvisibleRadius);
}
if(borderOptions.dashStyle) {
controller.doc.setLineDashPattern([]);
}
controller.doc.setDrawColor(oldDrawColor);
}
public static getLocString(text: LocalizableString): string {
Expand Down Expand Up @@ -672,20 +694,20 @@ export class SurveyHelper {
};
}
public static scaleRect(rect: IRect, scale: number): IRect {
const width: number = rect.xRight - rect.xLeft;
const height: number = rect.yBot - rect.yTop;
const scaleWidth: number = ((width < height) ? width : height) * (1.0 - scale) / 2.0;
const scaleWidth: number = Math.min(rect.xRight - rect.xLeft, rect.yBot - rect.yTop) * (1.0 - scale) / 2.0;
return {
xLeft: rect.xLeft + scaleWidth,
yTop: rect.yTop + scaleWidth,
xRight: rect.xRight - scaleWidth,
yBot: rect.yBot - scaleWidth
};
}
public static getBorderWidth(controller: DocController) {
return 2.0 * controller.unitWidth * this.BORDER_SCALE;
}
public static formScale(controller: DocController, flat: ISize): number {
const minSide: number = Math.min(flat.width, flat.height);
const borderWidth: number = 2.0 * controller.unitWidth * this.BORDER_SCALE;
return (minSide - borderWidth) / minSide;
return (minSide - this.getBorderWidth(controller)) / minSide;
}
public static async generateQuestionFlats(survey: SurveyPDF,
controller: DocController, question: Question, point: IPoint): Promise<IPdfBrick[]> {
Expand Down
33 changes: 33 additions & 0 deletions tests/pdf_signature.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { checkPDFSnapshot } from './snapshot_helper';
import { FlatSignaturePad } from '../src/flat_layout/flat_signaturepad';

var json = {
elements: [
{
type: 'signaturepad',
name: 'q1',
}
]
};

test('Check signature with dashed border', async () => {
await checkPDFSnapshot(json, {
snapshotName: 'signature_border_dashed',
});
});

test('Check signature with solid border', async () => {
FlatSignaturePad.BORDER_STYLE = 'solid';
await checkPDFSnapshot(json, {
snapshotName: 'signature_border_solid',
});
FlatSignaturePad.BORDER_STYLE = 'none';
});

test('Check signature without border', async () => {
FlatSignaturePad.BORDER_STYLE = 'none';
await checkPDFSnapshot(json, {
snapshotName: 'signature_border_none',
});
FlatSignaturePad.BORDER_STYLE = 'dashed';
});
Binary file added tests/pdf_snapshots/signature_border_dashed.pdf
Binary file not shown.
Binary file added tests/pdf_snapshots/signature_border_none.pdf
Binary file not shown.
Binary file added tests/pdf_snapshots/signature_border_solid.pdf
Binary file not shown.
4 changes: 3 additions & 1 deletion tests/snapshot_helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { EventBase } from 'survey-core';
import { DocController, FlatSurvey, IDocOptions, IPdfBrick } from '../src/entries/pdf';
import { FlatSurvey } from '../src/flat_layout/flat_survey';
import { DocController, IDocOptions } from '../src/doc_controller';
import { IPdfBrick } from '../src/pdf_render/pdf_brick';
import { SurveyPDFTester, TestHelper } from '../src/helper_test';
import { SurveyPDF } from '../src/survey';
import { readFileSync, writeFileSync } from 'fs';
Expand Down

0 comments on commit a891f9f

Please sign in to comment.