changes to preview calculations to make it more similar to the output

This commit is contained in:
EthanHealy01 2025-09-21 22:20:24 +01:00
parent 57a0f537b2
commit 6177c05b9e
2 changed files with 28 additions and 36 deletions

View File

@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
import { AddStampParameters } from './useAddStampParameters'; import { AddStampParameters } from './useAddStampParameters';
import { pdfWorkerManager } from '../../../services/pdfWorkerManager'; import { pdfWorkerManager } from '../../../services/pdfWorkerManager';
import { useThumbnailGeneration } from '../../../hooks/useThumbnailGeneration'; import { useThumbnailGeneration } from '../../../hooks/useThumbnailGeneration';
import { A4_ASPECT_RATIO, getFirstSelectedPage, getFontFamily, computeStampPreviewStyle } from './StampPreviewUtils'; import { A4_ASPECT_RATIO, getFirstSelectedPage, getFontFamily, computeStampPreviewStyle, getAlphabetPreviewScale } from './StampPreviewUtils';
import styles from './StampPreview.module.css'; import styles from './StampPreview.module.css';
type Props = { type Props = {
@ -281,7 +281,7 @@ export default function StampPreview({ parameters, onParameterChange, file, show
className={styles.textLine} className={styles.textLine}
style={{ style={{
fontFamily: getFontFamily(parameters.alphabet), fontFamily: getFontFamily(parameters.alphabet),
fontSize: `${Math.max(1, parameters.fontSize / 2)}px`, fontSize: `${Math.max(1, (parameters.fontSize * getAlphabetPreviewScale(parameters.alphabet)) / 2)}px`,
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
}} }}
> >

View File

@ -50,6 +50,20 @@ export const getFirstSelectedPage = (input: string): number => {
export type StampPreviewStyle = { container: any; item: any }; export type StampPreviewStyle = { container: any; item: any };
// Unified per-alphabet preview adjustments
export type Alphabet = 'roman' | 'arabic' | 'japanese' | 'korean' | 'chinese' | 'thai';
export type AlphabetTweaks = { scale: number; rowOffsetRem: [number, number, number]; lineHeight: number; capHeightRatio: number };
export const ALPHABET_PREVIEW_TWEAKS: Record<Alphabet, AlphabetTweaks> = {
// [top, middle, bottom] row offsets in rem
roman: { scale: 1.0/1.18, rowOffsetRem: [0, 1, 2.2], lineHeight: 1.28, capHeightRatio: 0.70 },
arabic: { scale: 1.2, rowOffsetRem: [0, 1.5, 2.5], lineHeight: 1, capHeightRatio: 0.68 },
japanese: { scale: 1/1.2, rowOffsetRem: [-0.1, 1, 2], lineHeight: 1, capHeightRatio: 0.72 },
korean: { scale: 1.0/1.05, rowOffsetRem: [-0.2, 0.5, 1.4], lineHeight: 1, capHeightRatio: 0.72 },
chinese: { scale: 1/1.2, rowOffsetRem: [0, 2, 2.8], lineHeight: 1, capHeightRatio: 0.72 },
thai: { scale: 1/1.2, rowOffsetRem: [-1, 0, .8], lineHeight: 1, capHeightRatio: 0.66 },
};
export const getAlphabetPreviewScale = (alphabet: string): number => (ALPHABET_PREVIEW_TWEAKS as any)[alphabet]?.scale ?? 1.0;
export function computeStampPreviewStyle( export function computeStampPreviewStyle(
parameters: AddStampParameters, parameters: AddStampParameters,
imageMeta: ImageMeta, imageMeta: ImageMeta,
@ -70,26 +84,9 @@ export function computeStampPreviewStyle(
const marginPts = (widthPts + heightPts) / 2 * (marginFactorMap[parameters.customMargin] ?? 0.035); const marginPts = (widthPts + heightPts) / 2 * (marginFactorMap[parameters.customMargin] ?? 0.035);
// Compute content dimensions // Compute content dimensions
const heightPtsContent = parameters.fontSize; // UI size in points const heightPtsContent = parameters.fontSize * getAlphabetPreviewScale(parameters.alphabet);
let widthPtsContent = heightPtsContent; let widthPtsContent = heightPtsContent;
// Approximate PDF cap height ratio per alphabet to mirror backend's calculateTextCapHeight usage
const getCapHeightRatio = (alphabet: string): number => {
switch (alphabet) {
case 'roman':
return 0.70; // Noto Sans/Helvetica ~0.7 em
case 'arabic':
return 0.68;
case 'thai':
return 0.66;
case 'japanese':
case 'korean':
case 'chinese':
return 0.72; // CJK glyph boxes
default:
return 0.70;
}
};
if (parameters.stampType === 'image' && imageMeta) { if (parameters.stampType === 'image' && imageMeta) {
const aspect = imageMeta.width / imageMeta.height; const aspect = imageMeta.width / imageMeta.height;
@ -109,8 +106,6 @@ export function computeStampPreviewStyle(
// Convert measured px width back to PDF points using horizontal scale // Convert measured px width back to PDF points using horizontal scale
widthPtsContent = measuredWidthPx / scaleX; widthPtsContent = measuredWidthPx / scaleX;
// Empirical tweak to better match PDFBox string width for Roman fonts
// PDFBox often yields ~8-12% narrower widths than browser canvas for the same font family
let adjustmentFactor = 1.0; let adjustmentFactor = 1.0;
switch (parameters.alphabet) { switch (parameters.alphabet) {
case 'roman': case 'roman':
@ -151,7 +146,7 @@ export function computeStampPreviewStyle(
if (parameters.overrideX >= 0 && parameters.overrideY >= 0) return parameters.overrideY; if (parameters.overrideX >= 0 && parameters.overrideY >= 0) return parameters.overrideY;
// For text, backend positions using cap height, not full font size // For text, backend positions using cap height, not full font size
const heightForY = parameters.stampType === 'text' const heightForY = parameters.stampType === 'text'
? heightPtsContent * getCapHeightRatio(parameters.alphabet) ? heightPtsContent * ((ALPHABET_PREVIEW_TWEAKS as any)[parameters.alphabet]?.capHeightRatio ?? 0.70)
: heightPtsContent; : heightPtsContent;
switch (Math.floor((position - 1) / 3)) { switch (Math.floor((position - 1) / 3)) {
case 0: // Top case 0: // Top
@ -167,31 +162,28 @@ export function computeStampPreviewStyle(
const xPts = calcX(); const xPts = calcX();
const yPts = calcY(); const yPts = calcY();
const xPx = xPts * scaleX; let xPx = xPts * scaleX;
let yPx = yPts * scaleY; let yPx = yPts * scaleY;
// Vertical correction: text appears lower in preview vs output for middle/bottom rows
if (parameters.stampType === 'text') { if (parameters.stampType === 'text') {
try { try {
const rootFontSizePx = parseFloat(getComputedStyle(document.documentElement).fontSize || '16') || 16; const rootFontSizePx = parseFloat(getComputedStyle(document.documentElement).fontSize || '16') || 16;
const middleRowOffsetPx = 1 * rootFontSizePx; const rowIndex = Math.floor((position - 1) / 3); // 0 top, 1 middle, 2 bottom
const bottomRowOffsetPx = 1.25 * rootFontSizePx; const offsets = (ALPHABET_PREVIEW_TWEAKS as any)[parameters.alphabet]?.rowOffsetRem ?? [0, 0, 0];
const rowIndex = Math.floor((position - 1) / 3); const offsetRem = offsets[rowIndex] ?? 0;
if (rowIndex === 1) { yPx += offsetRem * rootFontSizePx;
yPx += middleRowOffsetPx;
} else if (rowIndex === 2) {
yPx += bottomRowOffsetPx;
}
} catch (e) { } catch (e) {
console.error(e); // no-op
} }
} }
const widthPx = widthPtsContent * scaleX; const widthPx = widthPtsContent * scaleX;
const heightPx = heightPtsContent * scaleY; const heightPx = heightPtsContent * scaleY;
xPx = Math.max(0, Math.min(xPx, pageWidthPx - widthPx));
yPx = Math.max(0, Math.min(yPx, pageHeightPx - heightPx));
const opacity = Math.max(0, Math.min(1, parameters.opacity / 100)); const opacity = Math.max(0, Math.min(1, parameters.opacity / 100));
const displayOpacity = opacity; const displayOpacity = opacity;
// Horizontal alignment inside the preview item for text stamps
let alignItems: 'flex-start' | 'center' | 'flex-end' = 'flex-start'; let alignItems: 'flex-start' | 'center' | 'flex-end' = 'flex-start';
if (parameters.stampType === 'text') { if (parameters.stampType === 'text') {
const colIndex = position % 3; // 1: left, 2: center, 0: right const colIndex = position % 3; // 1: left, 2: center, 0: right
@ -229,7 +221,7 @@ export function computeStampPreviewStyle(
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
justifyContent: 'flex-start', justifyContent: 'flex-start',
lineHeight: 1, lineHeight: (ALPHABET_PREVIEW_TWEAKS as any)[parameters.alphabet]?.lineHeight ?? 1,
alignItems, alignItems,
cursor: showQuickGrid ? 'default' : 'move', cursor: showQuickGrid ? 'default' : 'move',
pointerEvents: showQuickGrid ? 'none' : 'auto', pointerEvents: showQuickGrid ? 'none' : 'auto',