Skip to content

Commit

Permalink
Merge branch 'dev' into feature/#569-add-filename-to-footer
Browse files Browse the repository at this point in the history
  • Loading branch information
brauliodiez committed Nov 24, 2024
2 parents d497c81 + 4f8b823 commit 14ff17e
Show file tree
Hide file tree
Showing 12 changed files with 130 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Layer } from 'konva/lib/Layer';
import { balanceSpacePerItem } from './balance-space';
import { calcTextWidth } from './calc-text-width';
import { calcTextDimensions } from '@/common/utils/calc-text-dimensions';

export const adjustTabWidths = (args: {
tabs: string[];
Expand Down Expand Up @@ -35,9 +35,14 @@ export const adjustTabWidths = (args: {
}
const arrangeTabsInfo = tabs.reduce(
(acc: OriginalTabInfo[], tab, index): OriginalTabInfo[] => {
const tabFullTextWidth =
calcTextWidth(tab, font.fontSize, font.fontFamily, konvaLayer) +
totalInnerXPadding;
const { width: textWidth } = calcTextDimensions(
tab,
font.fontSize,
font.fontFamily,
konvaLayer
);

const tabFullTextWidth = textWidth + totalInnerXPadding;
const desiredWidth = Math.max(totalMinTabSpace, tabFullTextWidth);
return [
...acc,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { calcTextDimensions } from '@/common/utils/calc-text-dimensions';
import { useCanvasContext } from '@/core/providers';
import { useEffect, useRef } from 'react';

export const useResizeOnFontSizeChange = (
id: string,
coords: { x: number; y: number },
text: string,
fontSize: number,
fontVariant: string,
multiline: boolean = false
) => {
const previousFontSize = useRef(fontSize);
const { updateShapeSizeAndPosition, stageRef } = useCanvasContext();
const konvaLayer = stageRef.current?.getLayers()[0];

useEffect(() => {
if (previousFontSize.current !== fontSize) {
previousFontSize.current = fontSize;

const paragraphLines = _extractParagraphLines(text);
const longestLine = _findLongestString(paragraphLines);

const { width, height } = calcTextDimensions(
multiline ? longestLine : text,
fontSize,
fontVariant,
konvaLayer
);

updateShapeSizeAndPosition(
id,
coords,
{
width: width * 1.15,
height: multiline
? _calcParagraphTotalHeight(height, 0.8, paragraphLines.length)
: height,
},
true
);
}
}, [fontSize]);
};

/* Hook Helper functions */
function _extractParagraphLines(multilineText: string) {
return multilineText.split(/\r?\n/).filter(line => line.trim() !== '');
}

function _findLongestString(stringsArray: string[]): string {
return stringsArray.reduce((longest, current) =>
current.length > longest.length ? current : longest
);
}

function _calcParagraphTotalHeight(
heightPerLine: number,
lineHeight: number = 1.3,
linesQty: number
) {
return heightPerLine * lineHeight * linesQty;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import { BASIC_SHAPE } from '../front-components/shape.const';
import { useGroupShapeProps } from '../mock-components.utils';
import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook';

const heading1SizeRestrictions: ShapeSizeRestrictions = {
minWidth: 40,
Expand Down Expand Up @@ -56,6 +57,8 @@ export const Heading1Shape = forwardRef<any, ShapeProps>((props, ref) => {
ref
);

useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant);

return (
<Group {...commonGroupProps} {...shapeProps}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import { BASIC_SHAPE } from '../front-components/shape.const';
import { useGroupShapeProps } from '../mock-components.utils';
import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook';

const heading2SizeRestrictions: ShapeSizeRestrictions = {
minWidth: 40,
Expand Down Expand Up @@ -56,6 +57,8 @@ export const Heading2Shape = forwardRef<any, ShapeProps>((props, ref) => {
ref
);

useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant);

return (
<Group {...commonGroupProps} {...shapeProps}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import { BASIC_SHAPE } from '../front-components/shape.const';
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import { useGroupShapeProps } from '../mock-components.utils';
import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook';

const heading3SizeRestrictions: ShapeSizeRestrictions = {
minWidth: 40,
Expand Down Expand Up @@ -57,6 +58,8 @@ export const Heading3Shape = forwardRef<any, ShapeProps>((props, ref) => {
ref
);

useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant);

return (
<Group {...commonGroupProps} {...shapeProps}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import { BASIC_SHAPE } from '../front-components/shape.const';
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import { useGroupShapeProps } from '../mock-components.utils';
import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook';

const linkSizeRestrictions: ShapeSizeRestrictions = {
minWidth: 40,
Expand Down Expand Up @@ -41,10 +42,8 @@ export const LinkShape = forwardRef<any, ShapeProps>((props, ref) => {

const { width: restrictedWidth, height: restrictedHeight } = restrictedSize;

const { textColor, textDecoration, fontSize, textAlignment } = useShapeProps(
otherProps,
BASIC_SHAPE
);
const { textColor, textDecoration, fontSize, textAlignment, fontVariant } =
useShapeProps(otherProps, BASIC_SHAPE);

const commonGroupProps = useGroupShapeProps(
props,
Expand All @@ -53,6 +52,8 @@ export const LinkShape = forwardRef<any, ShapeProps>((props, ref) => {
ref
);

useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant);

return (
<Group {...commonGroupProps} {...shapeProps}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import { BASIC_SHAPE } from '../front-components/shape.const';
import { useGroupShapeProps } from '../mock-components.utils';
import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook';

const normaltextSizeRestrictions: ShapeSizeRestrictions = {
minWidth: 40,
Expand Down Expand Up @@ -56,6 +57,8 @@ export const NormaltextShape = forwardRef<any, ShapeProps>((props, ref) => {
ref
);

useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant);

return (
<Group {...commonGroupProps} {...shapeProps}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import { BASIC_SHAPE } from '../front-components/shape.const';
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import { useGroupShapeProps } from '../mock-components.utils';
import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook';

const paragraphSizeRestrictions: ShapeSizeRestrictions = {
minWidth: 200,
Expand Down Expand Up @@ -40,7 +41,7 @@ export const ParagraphShape = forwardRef<any, ShapeProps>((props, ref) => {
);
const { width: restrictedWidth, height: restrictedHeight } = restrictedSize;

const { textColor, fontSize, textAlignment } = useShapeProps(
const { textColor, fontSize, textAlignment, fontVariant } = useShapeProps(
otherProps,
BASIC_SHAPE
);
Expand All @@ -52,6 +53,8 @@ export const ParagraphShape = forwardRef<any, ShapeProps>((props, ref) => {
ref
);

useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant, true);

return (
<Group {...commonGroupProps} {...shapeProps}>
<Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { fitSizeToShapeSizeRestrictions } from '@/common/utils/shapes/shape-rest
import { useShapeProps } from '../../shapes/use-shape-props.hook';
import { BASIC_SHAPE } from '../front-components/shape.const';
import { useGroupShapeProps } from '../mock-components.utils';
import { useResizeOnFontSizeChange } from './front-text-hooks/resize-fontsize-change.hook';

const smalltextSizeRestrictions: ShapeSizeRestrictions = {
minWidth: 40,
Expand Down Expand Up @@ -56,6 +57,8 @@ export const SmalltextShape = forwardRef<any, ShapeProps>((props, ref) => {
ref
);

useResizeOnFontSizeChange(id, { x, y }, text, fontSize, fontVariant);

return (
<Group {...commonGroupProps} {...shapeProps}>
<Text
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Layer } from 'konva/lib/Layer';

/**
* Virtually calculates the width that a text will occupy, by using a canvas.
* Virtually calculates the height and width that a text will occupy, by using a canvas.
* If a Konva Layer is provided, it will reuse the already existing canvas.
* Otherwise, it will create a canvas within the document, on the fly, to perform the measurement.
* Finaly, as a safety net, a very generic calculation is provided in case the other options are not available.
*/
export const calcTextWidth = (
export const calcTextDimensions = (
inputText: string,
fontSize: number,
fontfamily: string,
Expand All @@ -31,7 +31,10 @@ const _getTextWidthByKonvaMethod = (
) => {
const context = konvaLayer.getContext();
context.font = `${fontSize}px ${fontfamily}`;
return context.measureText(text).width;
const { width, fontBoundingBoxAscent, fontBoundingBoxDescent } =
context.measureText(text);
const totalHeight = fontBoundingBoxAscent + fontBoundingBoxDescent;
return { width, height: totalHeight };
};

const _getTextCreatingNewCanvas = (
Expand All @@ -43,8 +46,13 @@ const _getTextCreatingNewCanvas = (
const context = canvas.getContext('2d');
if (context) {
context.font = `${fontSize}px ${fontfamily}`;
return context.measureText(text).width;
const { width, fontBoundingBoxAscent, fontBoundingBoxDescent } =
context.measureText(text);
const height = fontBoundingBoxAscent + fontBoundingBoxDescent;
return { width, height };
}
const charAverageWidth = fontSize * 0.7;
return text.length * charAverageWidth + charAverageWidth * 0.8;
const width = text.length * charAverageWidth + charAverageWidth * 0.8;
const height = fontSize * 1.5;
return { width, height };
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const ImageBlackAndWhite: React.FC<Props> = props => {
<p>{label}</p>
<input
type="checkbox"
checked={imageBlackAndWhite}
onChange={() => onChange(!imageBlackAndWhite)}
className={classes.checkbox}
/>
Expand Down
21 changes: 20 additions & 1 deletion src/pods/toolbar/components/export-button/export-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import classes from '@/pods/toolbar/toolbar.pod.module.css';
import { Stage } from 'konva/lib/Stage';
import { calculateCanvasBounds } from './export-button.utils';
import { ToolbarButton } from '../toolbar-button';
import Konva from 'konva';

export const ExportButton = () => {
const { stageRef, shapes } = useCanvasContext();
Expand All @@ -21,20 +22,38 @@ export const ExportButton = () => {
stage.scale({ x: 1, y: 1 });
};

const applyFiltersToImages = (stage: Stage) => {
stage.find('Image').forEach(node => {
if (node.filters()?.includes(Konva.Filters.Grayscale)) {
node.cache({
x: 0,
y: 0,
width: node.width(),
height: node.height(),
pixelRatio: 2,
});
}
});
};

const handleExport = () => {
if (stageRef.current) {
const originalStage = stageRef.current;
const clonedStage = originalStage.clone();

applyFiltersToImages(clonedStage);
resetScale(clonedStage);

const bounds = calculateCanvasBounds(shapes);
const dataURL = clonedStage.toDataURL({
mimeType: 'image/png', // Change to jpeg to download as jpeg
mimeType: 'image/png', // Swap to 'image/jpeg' if necessary
x: bounds.x,
y: bounds.y,
width: bounds.width,
height: bounds.height,
pixelRatio: 2,
});

createDownloadLink(dataURL);
}
};
Expand Down

0 comments on commit 14ff17e

Please sign in to comment.