Files
edr-platform/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/utils/letter-page-dimensions.ts
natib21 e6e44e773b fix ui
2026-07-10 11:25:59 +00:00

133 lines
3.6 KiB
TypeScript

/** Matches backend pdf-generator A4 dimensions at 96 CSS px. */
export const PDF_PAGE_WIDTH_PX = 794;
export const PDF_PAGE_HEIGHT_PX = 1123;
export type LetterPageMarginsPx = {
top: number;
left: number;
right: number;
bottom: number;
};
export type LetterLanguage = "am" | "en";
export const DEFAULT_LETTER_PAGE_MARGINS_PX: LetterPageMarginsPx = {
top: 120,
left: 30,
right: 20,
bottom: 120,
};
export const CLOSING_ASSET_GAP_AFTER_SINCERELY_PX = 8;
export const ESTIMATED_SINCERELY_BLOCK_HEIGHT_PX = 28;
export type ClosingLayoutAnchor = "signatures" | "seal" | "stamp";
export type AbsoluteLayoutAnchor =
| ClosingLayoutAnchor
| "senderSignature"
| "senderStamp";
export function computeLetterContentAreaPx(
margins: LetterPageMarginsPx = DEFAULT_LETTER_PAGE_MARGINS_PX,
): { width: number; height: number } {
return {
width: Math.max(
1,
PDF_PAGE_WIDTH_PX - margins.left - margins.right,
),
height: Math.max(
1,
PDF_PAGE_HEIGHT_PX - margins.top - margins.bottom,
),
};
}
function ptToPx(pt: number): number {
return Math.round((pt * 96) / 72);
}
export function isClosingLayoutAnchor(
anchor: AbsoluteLayoutAnchor,
): anchor is ClosingLayoutAnchor {
return anchor === "signatures" || anchor === "seal" || anchor === "stamp";
}
export function estimateClosingBlockTopPx(
_margins: LetterPageMarginsPx = DEFAULT_LETTER_PAGE_MARGINS_PX,
): number {
return 480;
}
export function buildDefaultClosingAssetAnchors(
contentWidth: number,
language: LetterLanguage = "am",
): Record<ClosingLayoutAnchor, { top: number; left: number }> {
const sigWidth = ptToPx(96);
const sealWidth = 140;
const sigHeight = 50;
const gap = CLOSING_ASSET_GAP_AFTER_SINCERELY_PX;
const baseTop = ESTIMATED_SINCERELY_BLOCK_HEIGHT_PX + gap;
if (language === "en") {
const columnLeft = 0;
const sealLeft = columnLeft + sigWidth + gap;
return {
signatures: { top: baseTop, left: columnLeft },
seal: { top: baseTop - 12, left: sealLeft },
stamp: { top: baseTop + sigHeight + gap, left: columnLeft },
};
}
const sealLeft = contentWidth - sealWidth;
const columnLeft = sealLeft - gap - sigWidth;
return {
signatures: { top: baseTop, left: columnLeft },
seal: { top: baseTop - 12, left: sealLeft },
stamp: { top: baseTop + sigHeight + gap, left: columnLeft },
};
}
export function buildDefaultAbsoluteAnchors(
margins: LetterPageMarginsPx = DEFAULT_LETTER_PAGE_MARGINS_PX,
language: LetterLanguage = "am",
): Record<AbsoluteLayoutAnchor, { top: number; left: number }> {
const { width, height } = computeLetterContentAreaPx(margins);
const closing = buildDefaultClosingAssetAnchors(width, language);
return {
...closing,
senderSignature: {
top: Math.round(height * 0.12),
left: Math.round(width * 0.48),
},
senderStamp: {
top: Math.round(height * 0.12),
left: Math.round(width * 0.64),
},
};
}
export function migrateClosingAssetTopFromPageAbsolute(
top: number,
margins: LetterPageMarginsPx = DEFAULT_LETTER_PAGE_MARGINS_PX,
): number {
const { height } = computeLetterContentAreaPx(margins);
if (top <= height * 0.45) return top;
return top - estimateClosingBlockTopPx(margins);
}
export function closingAssetToCanvasTop(
top: number,
margins: LetterPageMarginsPx = DEFAULT_LETTER_PAGE_MARGINS_PX,
): number {
return top + estimateClosingBlockTopPx(margins);
}
export function closingAssetFromCanvasTop(
top: number,
margins: LetterPageMarginsPx = DEFAULT_LETTER_PAGE_MARGINS_PX,
): number {
return top - estimateClosingBlockTopPx(margins);
}