fix text in viewer
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
@font-face {
|
@font-face {
|
||||||
font-family: "irancell";
|
font-family: "irancell";
|
||||||
font-style: thin;
|
font-style: normal;
|
||||||
font-weight: 200;
|
font-weight: 200;
|
||||||
src: url("./irancell-extralight.ttf");
|
src: url("./irancell-extralight.ttf");
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,13 @@
|
|||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "irancell";
|
font-family: "irancell";
|
||||||
font-style: thin;
|
font-style: normal;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
src: url("./irancell-bold.ttf");
|
src: url("./irancell-bold.ttf");
|
||||||
}
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: "irancell";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("./irancell-bold.ttf");
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,14 +2,7 @@ import { useEffect, useLayoutEffect, useRef, useMemo, useState } from "react";
|
|||||||
import { Text, Group } from "react-konva";
|
import { Text, Group } from "react-konva";
|
||||||
import Konva from "konva";
|
import Konva from "konva";
|
||||||
import type { TextShapeProps } from "./types";
|
import type { TextShapeProps } from "./types";
|
||||||
|
import { getFontFamily } from "@/pages/editor/utils/fontFamily";
|
||||||
const getFontFamily = (fontFamily?: string): string => {
|
|
||||||
const fontMap: Record<string, string> = {
|
|
||||||
"1": "irancell",
|
|
||||||
"2": "irancell",
|
|
||||||
};
|
|
||||||
return fontMap[fontFamily || "1"] || "irancell";
|
|
||||||
};
|
|
||||||
|
|
||||||
const getFontWeight = (fontWeight?: string): string => {
|
const getFontWeight = (fontWeight?: string): string => {
|
||||||
if (!fontWeight) return "normal";
|
if (!fontWeight) return "normal";
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
const FONT_FAMILY_MAP: Record<string, string> = {
|
||||||
|
"1": "irancell",
|
||||||
|
"2": "irancell",
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Maps editor font option ids to CSS font-family names. */
|
||||||
|
export const getFontFamily = (fontFamily?: string): string => {
|
||||||
|
return FONT_FAMILY_MAP[fontFamily || "1"] || fontFamily || "irancell";
|
||||||
|
};
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/** CSS font-weight values that match irancell @font-face declarations. */
|
||||||
|
export const getCssFontWeight = (fontWeight?: string): number => {
|
||||||
|
if (!fontWeight || fontWeight === "normal") return 300;
|
||||||
|
if (fontWeight === "bold" || fontWeight === "bolder") return 600;
|
||||||
|
|
||||||
|
const n = parseInt(fontWeight, 10);
|
||||||
|
if (!Number.isNaN(n)) {
|
||||||
|
if (n >= 600) return 600;
|
||||||
|
if (n <= 200) return 200;
|
||||||
|
return 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 300;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const isSingleLineText = (text?: string): boolean => !text?.includes("\n");
|
||||||
|
|
||||||
|
/** Scaled width for viewer text; adds slack so CSS does not wrap tighter than Konva. */
|
||||||
|
export const getScaledTextWidth = (
|
||||||
|
width: number | undefined,
|
||||||
|
scale: number,
|
||||||
|
singleLine: boolean,
|
||||||
|
): number | undefined => {
|
||||||
|
if (!width) return undefined;
|
||||||
|
const scaled = Math.ceil(width * scale);
|
||||||
|
return singleLine ? scaled + 2 : scaled;
|
||||||
|
};
|
||||||
@@ -2,6 +2,8 @@ import { forwardRef, useCallback } from 'react';
|
|||||||
import { type PageData } from '../types';
|
import { type PageData } from '../types';
|
||||||
import type { EditorObject } from '@/pages/editor/store/editorStore';
|
import type { EditorObject } from '@/pages/editor/store/editorStore';
|
||||||
import { toCssLinearGradient } from '@/pages/editor/utils/gradient';
|
import { toCssLinearGradient } from '@/pages/editor/utils/gradient';
|
||||||
|
import { getFontFamily } from '@/pages/editor/utils/fontFamily';
|
||||||
|
import { getCssFontWeight, getScaledTextWidth, isSingleLineText } from '@/pages/editor/utils/textStyle';
|
||||||
import '@/pages/viewer/styles/entranceAnimations.css';
|
import '@/pages/viewer/styles/entranceAnimations.css';
|
||||||
import { mergeEntranceAnimationStyle } from '@/pages/viewer/utils/entranceAnimationStyle';
|
import { mergeEntranceAnimationStyle } from '@/pages/viewer/utils/entranceAnimationStyle';
|
||||||
import { useBookPageEntranceSequence } from '@/pages/viewer/hooks/useBookPageEntranceSequence';
|
import { useBookPageEntranceSequence } from '@/pages/viewer/hooks/useBookPageEntranceSequence';
|
||||||
@@ -122,8 +124,9 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
// برای text، opacity در رنگ اعمال میشود، پس باید opacity را از baseStyle حذف کنیم
|
// برای text، opacity در رنگ اعمال میشود، پس باید opacity را از baseStyle حذف کنیم
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const { opacity, ...textBaseStyle } = baseStyle;
|
const { opacity, ...textBaseStyle } = baseStyle;
|
||||||
const textWidth = obj.width ? obj.width * scale : undefined;
|
const singleLine = isSingleLineText(obj.text);
|
||||||
const textLeft = ((obj.x || 0) - (obj.width || 0)) * scale;
|
const textWidth = getScaledTextWidth(obj.width, scale, singleLine);
|
||||||
|
const textLeft = Math.round(((obj.x || 0) - (obj.width || 0)) * scale);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={obj.id || index}
|
key={obj.id || index}
|
||||||
@@ -133,15 +136,15 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
left: `${textLeft}px`,
|
left: `${textLeft}px`,
|
||||||
width: textWidth ? `${textWidth}px` : undefined,
|
width: textWidth ? `${textWidth}px` : undefined,
|
||||||
fontSize: `${(obj.fontSize || 16) * scale}px`,
|
fontSize: `${(obj.fontSize || 16) * scale}px`,
|
||||||
fontFamily: obj.fontFamily || 'irancell, sans-serif',
|
fontFamily: getFontFamily(obj.fontFamily),
|
||||||
fontWeight: obj.fontWeight || 'normal',
|
fontWeight: getCssFontWeight(obj.fontWeight),
|
||||||
lineHeight: obj.lineHeight ?? 1.2,
|
lineHeight: obj.lineHeight ?? 1.2,
|
||||||
textAlign: obj.textAlign ?? 'right',
|
textAlign: obj.textAlign ?? 'right',
|
||||||
direction: 'rtl',
|
direction: 'rtl',
|
||||||
color: getColorWithOpacity(obj.fill, obj.opacity),
|
color: getColorWithOpacity(obj.fill, obj.opacity),
|
||||||
whiteSpace: 'pre-wrap',
|
whiteSpace: singleLine ? 'nowrap' : 'pre-wrap',
|
||||||
wordBreak: 'break-word',
|
|
||||||
letterSpacing: obj.letterSpacing ? `${obj.letterSpacing * scale}px` : undefined,
|
letterSpacing: obj.letterSpacing ? `${obj.letterSpacing * scale}px` : undefined,
|
||||||
|
boxSizing: 'content-box',
|
||||||
},
|
},
|
||||||
obj,
|
obj,
|
||||||
index,
|
index,
|
||||||
@@ -210,7 +213,8 @@ const BookPage = forwardRef<HTMLDivElement, BookPageProps>(
|
|||||||
height: obj.height ? `${obj.height * scale}px` : 'auto',
|
height: obj.height ? `${obj.height * scale}px` : 'auto',
|
||||||
color: obj.fill || '#3b82f6',
|
color: obj.fill || '#3b82f6',
|
||||||
fontSize: `${(obj.fontSize || 16) * scale}px`,
|
fontSize: `${(obj.fontSize || 16) * scale}px`,
|
||||||
fontFamily: obj.fontFamily || 'irancell, sans-serif',
|
fontFamily: getFontFamily(obj.fontFamily),
|
||||||
|
fontWeight: getCssFontWeight(obj.fontWeight),
|
||||||
textDecoration: 'underline',
|
textDecoration: 'underline',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
|||||||
Reference in New Issue
Block a user