presenton/servers/nextjs/utils/pptx_models_utils.ts

243 lines
6.9 KiB
TypeScript

import { ElementAttributes } from "@/types/element_attibutes";
import {
PptxSlideModel,
PptxTextBoxModel,
PptxAutoShapeBoxModel,
PptxPictureBoxModel,
PptxConnectorModel,
PptxPositionModel,
PptxSpacingModel,
PptxFillModel,
PptxStrokeModel,
PptxShadowModel,
PptxFontModel,
PptxParagraphModel,
PptxPictureModel,
PptxObjectFitModel,
PptxBoxShapeEnum,
PptxObjectFitEnum
} from "@/types/pptx_models";
/**
* Converts ElementAttributes[][] to PptxSlideModel[]
* Each inner array represents elements on a slide
*/
export function convertElementAttributesToPptxSlides(
slidesAttributes: ElementAttributes[][],
backgroundColors?: (string | undefined)[]
): PptxSlideModel[] {
return slidesAttributes.map((slideElements, index) => {
const shapes = slideElements.map(element => {
return convertElementToPptxShape(element);
}).filter(Boolean); // Remove any null/undefined shapes
const slide: PptxSlideModel = {
shapes: shapes as (PptxTextBoxModel | PptxAutoShapeBoxModel | PptxConnectorModel | PptxPictureBoxModel)[]
};
// Add background color if available
if (backgroundColors && backgroundColors[index]) {
slide.background = {
color: backgroundColors[index]
};
}
return slide;
});
}
/**
* Converts a single ElementAttributes to the appropriate PPTX shape model
*/
function convertElementToPptxShape(
element: ElementAttributes
): PptxTextBoxModel | PptxAutoShapeBoxModel | PptxConnectorModel | PptxPictureBoxModel | null {
// Skip elements without position
if (!element.position) {
return null;
}
// Check if it's an image element
if (element.tagName === 'img' || element.className?.includes('image')) {
return convertToPictureBox(element);
}
// Check if it's a text element
if (element.innerText && element.innerText.trim().length > 0) {
return convertToTextBox(element);
}
// Check if it's a connector/line element
if (element.tagName === 'hr' || element.className?.includes('connector') || element.className?.includes('line')) {
return convertToConnector(element);
}
// Default to auto shape box for other elements
return convertToAutoShapeBox(element);
}
/**
* Converts element to PptxTextBoxModel
*/
function convertToTextBox(element: ElementAttributes): PptxTextBoxModel {
const position: PptxPositionModel = {
left: element.position?.left,
top: element.position?.top,
width: element.position?.width,
height: element.position?.height
};
const margin: PptxSpacingModel | undefined = element.margin ? {
top: element.margin.top,
bottom: element.margin.bottom,
left: element.margin.left,
right: element.margin.right
} : undefined;
const fill: PptxFillModel | undefined = element.background?.color ? {
color: element.background.color
} : undefined;
const font: PptxFontModel | undefined = element.font ? {
name: element.font.name,
size: element.font.size,
bold: element.font.weight ? element.font.weight >= 600 : undefined,
italic: element.font.italic,
color: element.font.color
} : undefined;
const paragraph: PptxParagraphModel = {
spacing: undefined,
alignment: element.textAlign,
font,
text: element.innerText
};
return {
margin,
fill,
position,
text_wrap: element.textWrap ?? true,
paragraphs: [paragraph]
};
}
/**
* Converts element to PptxAutoShapeBoxModel
*/
function convertToAutoShapeBox(element: ElementAttributes): PptxAutoShapeBoxModel {
const position: PptxPositionModel = {
left: element.position?.left,
top: element.position?.top,
width: element.position?.width,
height: element.position?.height
};
const margin: PptxSpacingModel | undefined = element.margin ? {
top: element.margin.top,
bottom: element.margin.bottom,
left: element.margin.left,
right: element.margin.right
} : undefined;
const fill: PptxFillModel | undefined = element.background?.color ? {
color: element.background.color
} : undefined;
const stroke: PptxStrokeModel | undefined = element.border?.color ? {
color: element.border.color,
thickness: element.border.width || 1
} : undefined;
const shadow: PptxShadowModel | undefined = element.shadow?.color ? {
radius: element.shadow.radius ?? 4,
offset: element.shadow.offset ? Math.sqrt(element.shadow.offset[0] ** 2 + element.shadow.offset[1] ** 2) : undefined,
color: element.shadow.color,
opacity: element.shadow.opacity,
angle: element.shadow.angle
} : undefined;
// Check if element has text content
const paragraphs: PptxParagraphModel[] | undefined = element.innerText ? [{
spacing: undefined,
alignment: element.textAlign,
font: element.font ? {
name: element.font.name,
size: element.font.size,
bold: element.font.weight ? element.font.weight >= 600 : undefined,
italic: element.font.italic,
color: element.font.color
} : undefined,
text: element.innerText
}] : undefined;
return {
margin,
fill,
stroke,
shadow,
position,
text_wrap: element.textWrap ?? true,
border_radius: element.borderRadius ? (Array.isArray(element.borderRadius) ? element.borderRadius[0] : element.borderRadius) : 0,
paragraphs
};
}
/**
* Converts element to PptxPictureBoxModel
*/
function convertToPictureBox(element: ElementAttributes): PptxPictureBoxModel {
const position: PptxPositionModel = {
left: element.position?.left,
top: element.position?.top,
width: element.position?.width,
height: element.position?.height
};
const margin: PptxSpacingModel | undefined = element.margin ? {
top: element.margin.top,
bottom: element.margin.bottom,
left: element.margin.left,
right: element.margin.right
} : undefined;
const objectFit: PptxObjectFitModel = {
fit: element.objectFit ? (element.objectFit as PptxObjectFitEnum) : PptxObjectFitEnum.CONTAIN
};
// Extract image path from element attributes
const picture: PptxPictureModel = {
is_network: element.imageSrc ? element.imageSrc.startsWith('http') : false,
path: element.imageSrc || ''
};
return {
position,
margin,
clip: element.clip ?? false,
overlay: element.overlay,
border_radius: element.borderRadius ? (Array.isArray(element.borderRadius) ? element.borderRadius : [element.borderRadius]) : undefined,
shape: element.shape ? (element.shape as PptxBoxShapeEnum) : PptxBoxShapeEnum.RECTANGLE,
object_fit: objectFit,
picture
};
}
/**
* Converts element to PptxConnectorModel
*/
function convertToConnector(element: ElementAttributes): PptxConnectorModel {
const position: PptxPositionModel = {
left: element.position?.left,
top: element.position?.top,
width: element.position?.width,
height: element.position?.height
};
return {
type: element.connectorType,
position,
thickness: element.border?.width || 1,
color: element.border?.color || element.background?.color || '#000000'
};
}