243 lines
6.9 KiB
TypeScript
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'
|
|
};
|
|
}
|