From 9504070b159aad3e72b62241ff76cb41b1a43277 Mon Sep 17 00:00:00 2001 From: Thomas Sevagen Date: Mon, 24 Feb 2025 19:46:30 +0100 Subject: [PATCH] feat(media): add audio icon SVG and enhance SlideMedia with media type detection feat(export): enhance media handling by copying media parts and relationships during slide export media audio stable media based on content-type feat(slide): refactor media icon creation and positioning for improved layout video stable --- backend/pom.xml | 8 +- .../magneto/controller/ExportController.java | 30 +- .../cgi/magneto/core/constants/Slideshow.java | 2 + .../fr/cgi/magneto/factory/SlideFactory.java | 2 +- .../fr/cgi/magneto/helper/SlideHelper.java | 619 +++++++++++++++++- .../model/properties/SlideProperties.java | 12 +- .../fr/cgi/magneto/model/slides/Slide.java | 3 +- .../cgi/magneto/model/slides/SlideBoard.java | 4 +- .../model/slides/SlideDescription.java | 4 +- .../cgi/magneto/model/slides/SlideFile.java | 4 +- .../cgi/magneto/model/slides/SlideLink.java | 4 +- .../cgi/magneto/model/slides/SlideMedia.java | 75 ++- .../cgi/magneto/model/slides/SlideText.java | 11 +- .../cgi/magneto/model/slides/SlideTitle.java | 4 +- .../service/impl/DefaultExportService.java | 76 ++- backend/src/main/resources/img/audio_icon.svg | 6 + 16 files changed, 792 insertions(+), 72 deletions(-) create mode 100644 backend/src/main/resources/img/audio_icon.svg diff --git a/backend/pom.xml b/backend/pom.xml index fadd609e..ff79dd1a 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 @@ -130,6 +131,11 @@ poi-ooxml ${apachePoiVersion} + + org.apache.poi + poi-ooxml-full + ${apachePoiVersion} + org.jsoup jsoup diff --git a/backend/src/main/java/fr/cgi/magneto/controller/ExportController.java b/backend/src/main/java/fr/cgi/magneto/controller/ExportController.java index 8306ef2c..5df80287 100644 --- a/backend/src/main/java/fr/cgi/magneto/controller/ExportController.java +++ b/backend/src/main/java/fr/cgi/magneto/controller/ExportController.java @@ -1,5 +1,15 @@ package fr.cgi.magneto.controller; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.entcore.common.controller.ControllerHelper; +import org.entcore.common.user.UserUtils; + import fr.cgi.magneto.core.constants.Field; import fr.cgi.magneto.helper.I18nHelper; import fr.cgi.magneto.service.ExportService; @@ -9,11 +19,6 @@ import fr.wseduc.webutils.I18n; import io.vertx.core.buffer.Buffer; import io.vertx.core.http.HttpServerRequest; -import org.entcore.common.controller.ControllerHelper; -import org.entcore.common.user.UserUtils; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; public class ExportController extends ControllerHelper { private final ExportService exportService; @@ -38,6 +43,21 @@ public void exportBoardToPPTX(HttpServerRequest request) { .putHeader("Content-Disposition", "attachment; filename=\"board.pptx\""); ByteArrayOutputStream out = new ByteArrayOutputStream(); + System.out.println("Parties du package avant sauvegarde :"); + OPCPackage opcPackage = ppt.getPackage(); + try { + for (PackagePart part : opcPackage.getParts()) { + System.out.println(part.getPartName()); + } + } catch (InvalidFormatException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + System.out.println("\nRelations du package :"); + for (PackageRelationship rel : opcPackage.getRelationships()) { + System.out.println(rel.getRelationshipType() + " : " + rel.getTargetURI()); + } ppt.write(out); request.response().end(Buffer.buffer(out.toByteArray())); } catch (IOException e) { diff --git a/backend/src/main/java/fr/cgi/magneto/core/constants/Slideshow.java b/backend/src/main/java/fr/cgi/magneto/core/constants/Slideshow.java index 2cab3df8..43ff19e5 100644 --- a/backend/src/main/java/fr/cgi/magneto/core/constants/Slideshow.java +++ b/backend/src/main/java/fr/cgi/magneto/core/constants/Slideshow.java @@ -5,6 +5,8 @@ public class Slideshow { public static final int MARGIN_LEFT = 140; public static final int MARGIN_TOP_TITLE = 40; public static final int WIDTH = 1000; + public static final int SLIDE_HEIGHT = 720; + public static final int SLIDE_WIDTH = 1280; // Constantes pour les titres public static final int TITLE_HEIGHT = 70; diff --git a/backend/src/main/java/fr/cgi/magneto/factory/SlideFactory.java b/backend/src/main/java/fr/cgi/magneto/factory/SlideFactory.java index cbe54f3d..0bd0877b 100644 --- a/backend/src/main/java/fr/cgi/magneto/factory/SlideFactory.java +++ b/backend/src/main/java/fr/cgi/magneto/factory/SlideFactory.java @@ -25,7 +25,7 @@ public Slide createSlide(SlideResourceType type, SlideProperties properties) { case VIDEO: case AUDIO: return new SlideMedia(properties.getTitle(), properties.getCaption(), - properties.getResourceData(), properties.getExtension()); + properties.getResourceData(), properties.getContentType()); case BOARD: return new SlideBoard( properties.getTitle(), properties.getDescription(), diff --git a/backend/src/main/java/fr/cgi/magneto/helper/SlideHelper.java b/backend/src/main/java/fr/cgi/magneto/helper/SlideHelper.java index 76d6ebed..51972293 100644 --- a/backend/src/main/java/fr/cgi/magneto/helper/SlideHelper.java +++ b/backend/src/main/java/fr/cgi/magneto/helper/SlideHelper.java @@ -1,6 +1,46 @@ package fr.cgi.magneto.helper; import fr.cgi.magneto.core.constants.Slideshow; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import javax.imageio.ImageIO; +import javax.xml.namespace.QName; + +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFPictureData; +import org.apache.poi.xslf.usermodel.XSLFPictureShape; +import org.apache.poi.xslf.usermodel.XSLFSlide; +import org.apache.poi.xslf.usermodel.XSLFTextBox; +import org.apache.poi.xslf.usermodel.XSLFTextParagraph; +import org.apache.poi.xslf.usermodel.XSLFTextRun; +import org.apache.poi.xslf.usermodel.XSLFTextShape; +import org.apache.xmlbeans.XmlCursor; +import static org.apache.poi.openxml4j.opc.PackageRelationshipTypes.CORE_PROPERTIES_ECMA376_NS; +import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; +import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; +import org.openxmlformats.schemas.presentationml.x2006.main.CTExtension; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; +import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; +import org.openxmlformats.schemas.presentationml.x2006.main.CTTLCommonMediaNodeData; +import org.openxmlformats.schemas.presentationml.x2006.main.CTTLCommonTimeNodeData; +import org.openxmlformats.schemas.presentationml.x2006.main.CTTimeNodeList; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeIndefinite; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeFillType; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeRestartType; +import org.openxmlformats.schemas.presentationml.x2006.main.STTLTimeNodeType; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.sl.usermodel.PictureData.PictureType; import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.PlaceholderDetails; @@ -60,10 +100,10 @@ public static XSLFTextBox createContent(XSLFSlide slide) { return contentBox; } - public static XSLFPictureShape createImage(XSLFSlide slide, byte[] pictureData, String extension, int contentMarginTop, int imageContentHeight) { + public static XSLFPictureShape createImage(XSLFSlide slide, byte[] pictureData, String fileContentType, int contentMarginTop, int imageContentHeight) { XMLSlideShow ppt = slide.getSlideShow(); - XSLFPictureData pic = ppt.addPicture(pictureData, getPictureTypeFromExtension(extension)); + XSLFPictureData pic = ppt.addPicture(pictureData, getPictureTypeFromContentType(fileContentType)); java.awt.Dimension imgSize = pic.getImageDimension(); double imgRatio = (double) imgSize.width / imgSize.height; @@ -86,18 +126,577 @@ public static XSLFPictureShape createImage(XSLFSlide slide, byte[] pictureData, return shape; } - private static PictureType getPictureTypeFromExtension(String extension) { - String cleanExt = extension.toLowerCase(); - if (!cleanExt.startsWith(".")) { - cleanExt = "." + cleanExt; + public static XSLFPictureShape createAudio(XSLFSlide slide, byte[] audioData, String fileContentType) { + System.out.println("=== DÉBUT CRÉATION AUDIO ==="); + String extension = getExtensionFromContentType(fileContentType); + try { + // Générer un nom pour le fichier audio + String audioFileName = "audio_" + System.currentTimeMillis() + "." + extension; + System.out.println("Nom fichier audio généré: " + audioFileName); + + // Créer et stocker le fichier audio + XMLSlideShow ppt = slide.getSlideShow(); + OPCPackage opcPackage = ppt.getPackage(); + System.out.println("Package récupéré: " + opcPackage); + + PackagePartName audioPartName = PackagingURIHelper.createPartName("/ppt/media/" + audioFileName); + System.out.println("Chemin audio créé: " + audioPartName); + + PackagePart audioPart = opcPackage.createPart(audioPartName, fileContentType); + System.out.println("Partie audio créée: " + audioPart); + + try (OutputStream out = audioPart.getOutputStream()) { + out.write(audioData); + System.out.println("Données audio écrites: " + audioData.length + " octets"); + } + + // Obtenir la partie du slide + PackagePart pp = slide.getPackagePart(); + System.out.println("Partie du slide: " + pp); + + // Créer deux relations vers le fichier audio + System.out.println("Création des relations..."); + PackageRelationship prsEmbed = pp.addRelationship( + audioPart.getPartName(), TargetMode.INTERNAL, + "http://schemas.microsoft.com/office/2007/relationships/media"); + System.out.println("Relation média créée: " + prsEmbed.getId() + " -> " + prsEmbed.getTargetURI()); + + PackageRelationship prsExec = pp.addRelationship( + audioPart.getPartName(), TargetMode.INTERNAL, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/audio"); + System.out.println("Relation audio créée: " + prsExec.getId() + " -> " + prsExec.getTargetURI()); + + // Créer l'icône de lecture + System.out.println("Création de l'icône..."); + byte[] iconData = getAudioIcon(); + System.out.println("Icône générée: " + (iconData != null ? iconData.length : 0) + " octets"); + + XSLFPictureData snap = ppt.addPicture(iconData, PictureType.PNG); + + System.out.println("Image ajoutée au PPT: " + snap.getFileName()); + + XSLFPictureShape pic = createAndPositionMediaIcon(slide, iconData); + System.out.println("Forme image créée, ID: " + pic.getShapeId()); + + // Configurer les propriétés de l'image pour le média + System.out.println("Configuration du XML..."); + CTPicture xpic = (CTPicture) pic.getXmlObject(); + System.out.println("XML de l'image récupéré"); + + CTHyperlink link = xpic.getNvPicPr().getCNvPr().addNewHlinkClick(); + link.setId(""); + link.setAction("ppaction://media"); + System.out.println("Lien hypertexte configuré: " + link.getAction()); + + // Ajouter les propriétés audio + CTApplicationNonVisualDrawingProps nvPr = xpic.getNvPicPr().getNvPr(); + nvPr.addNewAudioFile().setLink(prsExec.getId()); + System.out.println("Propriété audioFile configurée avec ID: " + prsExec.getId()); + + // Ajouter l'extension média + CTExtension ext = nvPr.addNewExtLst().addNewExt(); + ext.setUri("{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}"); + System.out.println("Extension ajoutée avec URI: " + ext.getUri()); + + // Configurer l'élément p14:media + String p14Ns = "http://schemas.microsoft.com/office/powerpoint/2010/main"; + try (XmlCursor cur = ext.newCursor()) { + cur.toEndToken(); + System.out.println("Curseur positionné à la fin de l'extension"); + cur.beginElement(new QName(p14Ns, "media", "p14")); + System.out.println("Élément p14:media créé"); + cur.insertNamespace("p14", p14Ns); + cur.insertNamespace("r", CORE_PROPERTIES_ECMA376_NS); + System.out.println("Namespaces p14 et r insérés"); + + // CHANGEMENT: Utiliser embed au lieu de link + System.out.println("Tentative d'insertion de l'attribut embed avec ID: " + prsEmbed.getId()); + cur.insertAttributeWithValue( + new QName(CORE_PROPERTIES_ECMA376_NS, "embed"), + prsEmbed.getId()); + System.out.println("Attribut embed inséré"); + } + + // S'assurer que le blipFill utilise le bon ID pour l'image + System.out.println("Vérification de la référence de l'image..."); + String imageRelId = slide.getRelationId(snap); + if (imageRelId != null) { + xpic.getBlipFill().getBlip().setEmbed(imageRelId); + System.out.println("BlipFill configuré avec ID d'image: " + imageRelId); + } else { + System.out.println("Avertissement: Impossible de trouver la relation pour l'image"); + } + + // Ajouter la section timing - CRUCIAL + System.out.println("Ajout des informations de timing..."); + CTSlide xslide = slide.getXmlObject(); + CTTimeNodeList ctnl; + + if (!xslide.isSetTiming()) { + System.out.println("Timing non défini, création..."); + CTTLCommonTimeNodeData ctn = xslide.addNewTiming().addNewTnLst().addNewPar().addNewCTn(); + // CHANGEMENT: Ajouter ID au nœud temporel racine + ctn.setId(1); + ctn.setDur(STTLTimeIndefinite.INDEFINITE); + ctn.setRestart(STTLTimeNodeRestartType.NEVER); + ctn.setNodeType(STTLTimeNodeType.TM_ROOT); + System.out.println("Timing root créé avec ID: 1"); + ctnl = ctn.addNewChildTnLst(); + System.out.println("Liste des nœuds enfants créée"); + } else { + System.out.println("Timing déjà défini, récupération..."); + ctnl = xslide.getTiming().getTnLst().getParArray(0).getCTn().getChildTnLst(); + } + + // Utiliser addNewAudio() au lieu de addNewVideo() + System.out.println("Ajout du nœud audio..."); + CTTLCommonMediaNodeData cmedia = ctnl.addNewAudio().addNewCMediaNode(); + cmedia.setVol(80000); + System.out.println("Volume configuré: 80000"); + + CTTLCommonTimeNodeData ctn = cmedia.addNewCTn(); + // CHANGEMENT: Ajouter ID au nœud temporel audio + ctn.setId(2); + ctn.setFill(STTLTimeNodeFillType.HOLD); + ctn.setDisplay(false); + System.out.println("Propriétés du nœud temporel configurées avec ID: 2"); + + ctn.addNewStCondLst().addNewCond().setDelay(STTLTimeIndefinite.INDEFINITE); + System.out.println("Condition de démarrage ajoutée"); + + cmedia.addNewTgtEl().addNewSpTgt().setSpid(pic.getShapeId()); + System.out.println("Cible du média configurée avec ID: " + pic.getShapeId()); + + // Afficher le XML pour le débogage + System.out.println("XML final du slide: " + xslide); + + System.out.println("=== CRÉATION AUDIO TERMINÉE AVEC SUCCÈS ==="); + return pic; + } catch (Exception e) { + System.out.println("=== ERREUR LORS DE LA CRÉATION AUDIO ==="); + e.printStackTrace(); + return null; } + } + + public static XSLFPictureShape createVideo(XSLFSlide slide, byte[] videoData, String fileContentType) { + System.out.println("=== DÉBUT CRÉATION VIDÉO ==="); + String extension = getExtensionFromContentType(fileContentType); + try { + // Générer un nom pour le fichier vidéo + String videoFileName = "video_" + System.currentTimeMillis() + "." + extension; + System.out.println("Nom fichier vidéo généré: " + videoFileName); + + // Créer et stocker le fichier vidéo + XMLSlideShow ppt = slide.getSlideShow(); + OPCPackage opcPackage = ppt.getPackage(); + System.out.println("Package récupéré: " + opcPackage); + + PackagePartName videoPartName = PackagingURIHelper.createPartName("/ppt/media/" + videoFileName); + System.out.println("Chemin vidéo créé: " + videoPartName); + + PackagePart videoPart = opcPackage.createPart(videoPartName, fileContentType); + System.out.println("Partie vidéo créée: " + videoPart); + + try (OutputStream out = videoPart.getOutputStream()) { + out.write(videoData); + System.out.println("Données vidéo écrites: " + videoData.length + " octets"); + } + + // Obtenir la partie du slide + PackagePart pp = slide.getPackagePart(); + System.out.println("Partie du slide: " + pp); + + // Créer deux relations vers le fichier vidéo + System.out.println("Création des relations..."); + PackageRelationship prsEmbed = pp.addRelationship( + videoPart.getPartName(), TargetMode.INTERNAL, + "http://schemas.microsoft.com/office/2007/relationships/media"); + System.out.println("Relation média créée: " + prsEmbed.getId() + " -> " + prsEmbed.getTargetURI()); + + PackageRelationship prsExec = pp.addRelationship( + videoPart.getPartName(), TargetMode.INTERNAL, + "http://schemas.openxmlformats.org/officeDocument/2006/relationships/video"); + System.out.println("Relation vidéo créée: " + prsExec.getId() + " -> " + prsExec.getTargetURI()); + + // Créer une miniature de la vidéo + System.out.println("Création de la miniature..."); + byte[] thumbnailData = getVideoThumbnail(videoData, extension); + System.out.println("Miniature générée: " + (thumbnailData != null ? thumbnailData.length : 0) + " octets"); + + XSLFPictureData snap = ppt.addPicture(thumbnailData, PictureType.PNG); + System.out.println("Image ajoutée au PPT: " + snap.getFileName()); + + // Positionner la miniature vidéo dans le slide + XSLFPictureShape pic = createAndPositionVideoThumbnail(slide, thumbnailData); + System.out.println("Forme image créée, ID: " + pic.getShapeId()); - for (PictureType type : PictureType.values()) { - if (type.extension.equals(cleanExt)) { - return type; + // Configurer les propriétés de l'image pour le média + System.out.println("Configuration du XML..."); + CTPicture xpic = (CTPicture) pic.getXmlObject(); + System.out.println("XML de l'image récupéré"); + + CTHyperlink link = xpic.getNvPicPr().getCNvPr().addNewHlinkClick(); + link.setId(""); + link.setAction("ppaction://media"); + System.out.println("Lien hypertexte configuré: " + link.getAction()); + + // Ajouter les propriétés vidéo + CTApplicationNonVisualDrawingProps nvPr = xpic.getNvPicPr().getNvPr(); + // Pour vidéo, utiliser videoFile au lieu de audioFile + nvPr.addNewVideoFile().setLink(prsExec.getId()); + System.out.println("Propriété videoFile configurée avec ID: " + prsExec.getId()); + + // Ajouter l'extension média + CTExtension ext = nvPr.addNewExtLst().addNewExt(); + ext.setUri("{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}"); + System.out.println("Extension ajoutée avec URI: " + ext.getUri()); + + // Configurer l'élément p14:media + String p14Ns = "http://schemas.microsoft.com/office/powerpoint/2010/main"; + try (XmlCursor cur = ext.newCursor()) { + cur.toEndToken(); + System.out.println("Curseur positionné à la fin de l'extension"); + cur.beginElement(new QName(p14Ns, "media", "p14")); + System.out.println("Élément p14:media créé"); + cur.insertNamespace("p14", p14Ns); + cur.insertNamespace("r", CORE_PROPERTIES_ECMA376_NS); + System.out.println("Namespaces p14 et r insérés"); + + System.out.println("Tentative d'insertion de l'attribut embed avec ID: " + prsEmbed.getId()); + cur.insertAttributeWithValue( + new QName(CORE_PROPERTIES_ECMA376_NS, "embed"), + prsEmbed.getId()); + System.out.println("Attribut embed inséré"); + } + + // S'assurer que le blipFill utilise le bon ID pour l'image + System.out.println("Vérification de la référence de l'image..."); + String imageRelId = slide.getRelationId(snap); + if (imageRelId != null) { + xpic.getBlipFill().getBlip().setEmbed(imageRelId); + System.out.println("BlipFill configuré avec ID d'image: " + imageRelId); + } else { + System.out.println("Avertissement: Impossible de trouver la relation pour l'image"); } + + // Ajouter la section timing - CRUCIAL + System.out.println("Ajout des informations de timing..."); + CTSlide xslide = slide.getXmlObject(); + CTTimeNodeList ctnl; + + if (!xslide.isSetTiming()) { + System.out.println("Timing non défini, création..."); + CTTLCommonTimeNodeData ctn = xslide.addNewTiming().addNewTnLst().addNewPar().addNewCTn(); + ctn.setId(1); + ctn.setDur(STTLTimeIndefinite.INDEFINITE); + ctn.setRestart(STTLTimeNodeRestartType.NEVER); + ctn.setNodeType(STTLTimeNodeType.TM_ROOT); + System.out.println("Timing root créé avec ID: 1"); + ctnl = ctn.addNewChildTnLst(); + System.out.println("Liste des nœuds enfants créée"); + } else { + System.out.println("Timing déjà défini, récupération..."); + ctnl = xslide.getTiming().getTnLst().getParArray(0).getCTn().getChildTnLst(); + } + + // Pour vidéo, utiliser addNewVideo() au lieu de addNewAudio() + System.out.println("Ajout du nœud vidéo..."); + CTTLCommonMediaNodeData cmedia = ctnl.addNewVideo().addNewCMediaNode(); + cmedia.setVol(80000); // Garder le volume comme pour l'audio + System.out.println("Volume configuré: 80000"); + + CTTLCommonTimeNodeData ctn = cmedia.addNewCTn(); + ctn.setId(2); + ctn.setFill(STTLTimeNodeFillType.HOLD); + ctn.setDisplay(false); + System.out.println("Propriétés du nœud temporel configurées avec ID: 2"); + + ctn.addNewStCondLst().addNewCond().setDelay(STTLTimeIndefinite.INDEFINITE); + System.out.println("Condition de démarrage ajoutée"); + + cmedia.addNewTgtEl().addNewSpTgt().setSpid(pic.getShapeId()); + System.out.println("Cible du média configurée avec ID: " + pic.getShapeId()); + + // Afficher le XML pour le débogage + System.out.println("XML final du slide: " + xslide); + + System.out.println("=== CRÉATION VIDÉO TERMINÉE AVEC SUCCÈS ==="); + return pic; + } catch (Exception e) { + System.out.println("=== ERREUR LORS DE LA CRÉATION VIDÉO ==="); + e.printStackTrace(); + return null; + } + } + + private static PictureType getPictureTypeFromContentType(String contentType) { + if (contentType == null) { + return PictureType.PNG; + } + + String lowerContentType = contentType.toLowerCase(); + + switch (lowerContentType) { + case "image/jpeg": + case "image/jpg": + return PictureType.JPEG; + case "image/png": + return PictureType.PNG; + case "image/gif": + return PictureType.GIF; + case "image/tiff": + return PictureType.TIFF; + case "image/x-emf": + return PictureType.EMF; + case "image/x-wmf": + return PictureType.WMF; + case "image/x-pict": + return PictureType.PICT; + case "image/dib": + return PictureType.DIB; + case "image/x-eps": + return PictureType.EPS; + case "image/x-ms-bmp": + case "image/bmp": + return PictureType.BMP; + case "image/x-wpg": + return PictureType.WPG; + case "image/vnd.ms-photo": + return PictureType.WDP; + case "image/svg+xml": + return PictureType.SVG; + default: + System.out.println("Content type non reconnu: " + contentType + ", utilisation de PNG par défaut"); + return PictureType.PNG; + } + } + + private static XSLFPictureShape createAndPositionMediaIcon(XSLFSlide slide, byte[] iconData) { + System.out.println("Création et positionnement de l'icône audio..."); + + XMLSlideShow ppt = slide.getSlideShow(); + XSLFPictureData snap = ppt.addPicture(iconData, PictureType.PNG); + System.out.println("Image ajoutée au PPT: " + snap.getFileName()); + + XSLFPictureShape pic = slide.createPicture(snap); + System.out.println("Forme image créée, ID: " + pic.getShapeId()); + + // Définir une taille plus grande + int iconWidth = 150; + int iconHeight = 150; + + // Calculer la position verticale centrée + int y = (Slideshow.SLIDE_HEIGHT - iconHeight) / 2; // Centre vertical + + // Utiliser le MARGIN_LEFT existant pour l'alignement horizontal + pic.setAnchor(new Rectangle(Slideshow.MARGIN_LEFT, y, iconWidth, iconHeight)); + System.out.println("Position de l'icône ajustée: x=" + Slideshow.MARGIN_LEFT + ", y=" + y + + ", width=" + iconWidth + ", height=" + iconHeight); + + return pic; + } + + private static byte[] getAudioIcon() { + try { + // Créer une image de 100x100 pixels avec un fond transparent + BufferedImage image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_ARGB); + + // Obtenir le contexte graphique + Graphics2D g2d = image.createGraphics(); + + // Activer l'antialiasing pour des bords plus lisses + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // Dessiner un cercle comme fond (bleu clair) + g2d.setColor(new Color(0, 120, 215, 240)); + g2d.fillOval(5, 5, 90, 90); + + // Dessiner un triangle de lecture (blanc) + g2d.setColor(Color.WHITE); + int[] xPoints = { 35, 70, 35 }; + int[] yPoints = { 30, 50, 70 }; + g2d.fillPolygon(xPoints, yPoints, 3); + + // Libérer les ressources + g2d.dispose(); + + // Convertir l'image en tableau de bytes (PNG) + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "png", baos); + return baos.toByteArray(); + + } catch (IOException e) { + e.printStackTrace(); + + // En cas d'erreur, retourner un tableau d'octets minimal pour un fichier PNG + // (Cela créera une image minuscule mais valide) + return new byte[] { + (byte) 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00, 0x00, 0x00, 0x0D, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, + 0x02, 0x00, 0x00, 0x00, (byte) 0x90, 0x77, 0x53, (byte) 0xDE, 0x00, 0x00, 0x00, + 0x0C, 0x49, 0x44, 0x41, 0x54, 0x08, (byte) 0xD7, 0x63, (byte) 0xF8, (byte) 0xCF, + (byte) 0xC0, 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, (byte) 0x18, (byte) 0xDD, (byte) 0x8D, + (byte) 0xB0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, (byte) 0xAE, 0x42, + 0x60, (byte) 0x82 + }; + } + } + + private static XSLFPictureShape createAndPositionVideoThumbnail(XSLFSlide slide, byte[] thumbnailData) { + System.out.println("Création et positionnement de la miniature vidéo..."); + + XMLSlideShow ppt = slide.getSlideShow(); + XSLFPictureData snap = ppt.addPicture(thumbnailData, PictureType.PNG); + System.out.println("Image ajoutée au PPT: " + snap.getFileName()); + + XSLFPictureShape pic = slide.createPicture(snap); + System.out.println("Forme image créée, ID: " + pic.getShapeId()); + + // Définir une taille adaptée pour la vidéo + int videoWidth = 640; + int videoHeight = 360; // Format 16:9 standard + + // Calculer la position centrée + int x = (Slideshow.SLIDE_WIDTH - videoWidth) / 2; // Centre horizontal + int y = (Slideshow.SLIDE_HEIGHT - videoHeight) / 2; // Centre vertical + + pic.setAnchor(new Rectangle(x, y, videoWidth, videoHeight)); + System.out.println("Position de la miniature ajustée: x=" + x + ", y=" + y + + ", width=" + videoWidth + ", height=" + videoHeight); + + return pic; + } + + private static byte[] getVideoThumbnail(byte[] videoData, String extension) { + try { + // Dans un cas réel, on extrairait une image de la vidéo ici + // Pour cet exemple, nous allons simplement créer une vignette générique + + // Créer une image de 640x360 pixels (format 16:9) + BufferedImage image = new BufferedImage(640, 360, BufferedImage.TYPE_INT_RGB); + + // Obtenir le contexte graphique + Graphics2D g2d = image.createGraphics(); + + // Activer l'antialiasing pour des bords plus lisses + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // Remplir le fond avec un dégradé bleu foncé + g2d.setColor(new Color(20, 20, 50)); + g2d.fillRect(0, 0, 640, 360); + + // Dessiner un symbole de lecture au centre + g2d.setColor(new Color(255, 255, 255, 180)); + int centerX = 640 / 2; + int centerY = 360 / 2; + int radius = 50; + g2d.fillOval(centerX - radius, centerY - radius, radius * 2, radius * 2); + + // Triangle de lecture + g2d.setColor(new Color(20, 20, 50)); + int[] xPoints = { centerX - 15, centerX + 25, centerX - 15 }; + int[] yPoints = { centerY - 25, centerY, centerY + 25 }; + g2d.fillPolygon(xPoints, yPoints, 3); + + // Ajouter le texte "VIDÉO" + g2d.setColor(Color.WHITE); + g2d.setFont(new java.awt.Font("Arial", java.awt.Font.BOLD, 24)); + java.awt.FontMetrics fm = g2d.getFontMetrics(); + String text = "VIDÉO"; + int textWidth = fm.stringWidth(text); + g2d.drawString(text, centerX - textWidth / 2, centerY + 80); + + // Libérer les ressources + g2d.dispose(); + + // Convertir l'image en tableau de bytes (PNG) + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "png", baos); + return baos.toByteArray(); + + } catch (IOException e) { + e.printStackTrace(); + return getDefaultThumbnail(); } + } + + private static byte[] getDefaultThumbnail() { + try { + // Créer une image simple par défaut + BufferedImage image = new BufferedImage(320, 180, BufferedImage.TYPE_INT_RGB); + Graphics2D g2d = image.createGraphics(); + g2d.setColor(Color.DARK_GRAY); + g2d.fillRect(0, 0, 320, 180); + g2d.setColor(Color.WHITE); + g2d.setFont(new java.awt.Font("Arial", java.awt.Font.BOLD, 18)); + g2d.drawString("Video Preview", 100, 90); + g2d.dispose(); - return PictureType.PNG; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "png", baos); + return baos.toByteArray(); + } catch (IOException e) { + e.printStackTrace(); + return new byte[0]; + } + } + + private static String getExtensionFromContentType(String contentType) { + if (contentType == null) { + return "mp4"; // Par défaut général + } + + String lowerContentType = contentType.toLowerCase(); + + // Types audio + switch (lowerContentType) { + // Types audio + case "audio/mpeg": + case "audio/mp3": + return "mp3"; + case "audio/wav": + case "audio/x-wav": + return "wav"; + case "audio/mp4": + case "audio/x-m4a": + return "m4a"; + case "audio/ogg": + return "ogg"; + + // Types vidéo + case "video/mp4": + return "mp4"; + case "video/mpeg": + return "mpg"; + case "video/x-ms-wmv": + return "wmv"; + case "video/quicktime": + return "mov"; + case "video/x-matroska": + return "mkv"; + case "video/webm": + return "webm"; + case "video/x-flv": + return "flv"; + case "video/3gpp": + return "3gp"; + case "video/avi": + case "video/x-msvideo": + return "avi"; + + default: + // Déterminer le type de média par préfixe + if (lowerContentType.startsWith("audio/")) { + System.out.println("Type audio non reconnu: " + contentType + ", utilisation de .mp3 par défaut"); + return "mp3"; + } else if (lowerContentType.startsWith("video/")) { + System.out.println("Type vidéo non reconnu: " + contentType + ", utilisation de .mp4 par défaut"); + return "mp4"; + } else { + System.out.println("Type de média non reconnu: " + contentType); + return null; // Retourner null pour les types non reconnus + } + } } } \ No newline at end of file diff --git a/backend/src/main/java/fr/cgi/magneto/model/properties/SlideProperties.java b/backend/src/main/java/fr/cgi/magneto/model/properties/SlideProperties.java index 4b0f5c2b..54b8ccfa 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/properties/SlideProperties.java +++ b/backend/src/main/java/fr/cgi/magneto/model/properties/SlideProperties.java @@ -9,9 +9,9 @@ public class SlideProperties { private String content; private String resourceUrl; private String resourceId; - private String extension; private String fileName; private byte[] resourceData; + private String contentType; private String ownerName; private String modificationDate; @@ -59,8 +59,8 @@ public Builder resourceId(String resourceId) { return this; } - public Builder extension(String extension) { - properties.extension = extension; + public Builder contentType(String contentType) { + properties.contentType = contentType; return this; } @@ -143,7 +143,7 @@ private boolean isValidForLink() { } private boolean isValidForMedia() { - return title != null && caption != null && extension != null && resourceData != null; + return title != null && caption != null && contentType != null && resourceData != null; } private boolean isValidForBoard() { @@ -180,8 +180,8 @@ public String getResourceId() { return resourceId; } - public String getExtension() { - return extension; + public String getContentType() { + return contentType; } public String getFileName() { diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/Slide.java b/backend/src/main/java/fr/cgi/magneto/model/slides/Slide.java index 85b57075..fb5b10f1 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/Slide.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/Slide.java @@ -1,9 +1,10 @@ package fr.cgi.magneto.model.slides; +import org.apache.poi.xslf.usermodel.XSLFSlide; public abstract class Slide { protected String title; protected String description = ""; - public abstract Object createApacheSlide(); + public abstract Object createApacheSlide(XSLFSlide newSlide); } diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideBoard.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideBoard.java index d31d29b8..78b62b87 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideBoard.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideBoard.java @@ -1,5 +1,7 @@ package fr.cgi.magneto.model.slides; +import org.apache.poi.xslf.usermodel.XSLFSlide; + public class SlideBoard extends Slide { private final String ownerName; private final String modificationDate; @@ -19,7 +21,7 @@ public SlideBoard(String title, String description, String ownerName, String mod } @Override - public Object createApacheSlide() { + public Object createApacheSlide(XSLFSlide newSlide) { return null; } } diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideDescription.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideDescription.java index ab3eb434..36f00dd0 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideDescription.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideDescription.java @@ -1,5 +1,7 @@ package fr.cgi.magneto.model.slides; +import org.apache.poi.xslf.usermodel.XSLFSlide; + public class SlideDescription extends Slide { public SlideDescription(String description) { @@ -7,7 +9,7 @@ public SlideDescription(String description) { } @Override - public Object createApacheSlide() { + public Object createApacheSlide(XSLFSlide newSlide) { return null; } } diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideFile.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideFile.java index 925a3e14..5c070392 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideFile.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideFile.java @@ -1,5 +1,7 @@ package fr.cgi.magneto.model.slides; +import org.apache.poi.xslf.usermodel.XSLFSlide; + public class SlideFile extends Slide { private final String fileName; private final String desc; @@ -10,7 +12,7 @@ public SlideFile(String fileName, String desc) { } @Override - public Object createApacheSlide() { + public Object createApacheSlide(XSLFSlide newSlide) { return null; } } diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideLink.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideLink.java index 6e4fbc85..5764a761 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideLink.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideLink.java @@ -1,5 +1,7 @@ package fr.cgi.magneto.model.slides; +import org.apache.poi.xslf.usermodel.XSLFSlide; + public class SlideLink extends Slide { private final String link; @@ -8,7 +10,7 @@ public SlideLink(String link) { } @Override - public Object createApacheSlide() { + public Object createApacheSlide(XSLFSlide newSlide) { return null; } } diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideMedia.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideMedia.java index 68cae763..dff51407 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideMedia.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideMedia.java @@ -8,28 +8,81 @@ public class SlideMedia extends Slide { + public enum MediaType { + IMAGE, + AUDIO, + VIDEO + } + private byte[] resourceData; - private final String fileExtension; + private final String fileContentType; private final String caption; + private final MediaType mediaType; public SlideMedia(String title, String caption, byte[] resourceData, - String fileExtension) { + String fileContentType) { this.title = title; this.caption = caption; this.resourceData = resourceData; - this.fileExtension = fileExtension; + this.fileContentType = fileContentType; + this.mediaType = determineMediaType(fileContentType); + + // Log des informations dans le constructeur + System.out.println("SlideMedia - Construction:"); + System.out.println("Title: " + title); + System.out.println("Caption: " + caption); + System.out.println("File extension: " + fileContentType); + System.out.println( + "Resource data present: " + (resourceData != null ? "Yes (" + resourceData.length + " bytes)" : "No")); + System.out.println("Determined media type: " + mediaType); } - @Override - public Object createApacheSlide() { + private MediaType determineMediaType(String contentType) { + String type = contentType.toLowerCase(); + MediaType mediaType; + + if (type.startsWith("audio/")) { + mediaType = MediaType.AUDIO; + } else if (type.startsWith("video/")) { + mediaType = MediaType.VIDEO; + } else { + mediaType = MediaType.IMAGE; + } - XMLSlideShow ppt = new XMLSlideShow(); - XSLFSlide slide = ppt.createSlide(); + // Log du type de média déterminé + System.out.println("determineMediaType - Content Type: " + contentType + " -> Type: " + mediaType); + + return mediaType; + } + + @Override + public Object createApacheSlide(XSLFSlide newSlide) { + // Log au début de la création de la slide + System.out.println("\nStarting createApacheSlide:"); + System.out.println("Title: " + title); + System.out.println("Media type: " + mediaType); + System.out.println("Resource data size: " + (resourceData != null ? resourceData.length : "null") + " bytes"); + System.out.println("File extension: " + fileContentType); - SlideHelper.createTitle(slide, title, Slideshow.TITLE_HEIGHT, Slideshow.TITLE_FONT_SIZE, TextParagraph.TextAlign.LEFT); - SlideHelper.createImage(slide, resourceData, fileExtension, Slideshow.CONTENT_MARGIN_TOP, Slideshow.IMAGE_CONTENT_HEIGHT); - SlideHelper.createLegend(slide, caption); + SlideHelper.createTitle(newSlide, title, Slideshow.TITLE_HEIGHT, Slideshow.TITLE_FONT_SIZE, + TextParagraph.TextAlign.LEFT); + switch (mediaType) { + case AUDIO: + System.out.println("Creating audio slide..."); + SlideHelper.createAudio(newSlide, resourceData, fileContentType); + break; + case VIDEO: + System.out.println("Creating video slide..."); + SlideHelper.createVideo(newSlide, resourceData, fileContentType); + break; + default: + System.out.println("Creating image slide..."); + SlideHelper.createImage(newSlide, resourceData, fileContentType, Slideshow.CONTENT_MARGIN_TOP, + Slideshow.IMAGE_CONTENT_HEIGHT); + } + SlideHelper.createLegend(newSlide, caption); - return slide; + System.out.println("Slide creation completed."); + return newSlide; } } \ No newline at end of file diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java index e35c6251..716bb34d 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideText.java @@ -20,17 +20,16 @@ public SlideText(String title, String description) { } @Override - public Object createApacheSlide() { - XMLSlideShow ppt = new XMLSlideShow(); - XSLFSlide slide = ppt.createSlide(); + public Object createApacheSlide(XSLFSlide newSlide) { - SlideHelper.createTitle(slide, title, Slideshow.TITLE_HEIGHT, Slideshow.TITLE_FONT_SIZE, TextParagraph.TextAlign.LEFT); - XSLFTextBox contentBox = SlideHelper.createContent(slide); + SlideHelper.createTitle(newSlide, title, Slideshow.TITLE_HEIGHT, Slideshow.TITLE_FONT_SIZE, + TextParagraph.TextAlign.LEFT); + XSLFTextBox contentBox = SlideHelper.createContent(newSlide); Document doc = Jsoup.parse(description); processHtmlContent(contentBox, doc.body()); - return slide; + return newSlide; } private void processHtmlContent(XSLFTextBox textBox, Element element) { diff --git a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideTitle.java b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideTitle.java index ea252ceb..66cfd7c1 100644 --- a/backend/src/main/java/fr/cgi/magneto/model/slides/SlideTitle.java +++ b/backend/src/main/java/fr/cgi/magneto/model/slides/SlideTitle.java @@ -1,5 +1,7 @@ package fr.cgi.magneto.model.slides; +import org.apache.poi.xslf.usermodel.XSLFSlide; + public class SlideTitle extends Slide { private final String title; @@ -8,7 +10,7 @@ public SlideTitle(String title) { } @Override - public Object createApacheSlide() { + public Object createApacheSlide(XSLFSlide newSlide) { return null; } } \ No newline at end of file diff --git a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java index 4fd98143..5ac8786c 100644 --- a/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java +++ b/backend/src/main/java/fr/cgi/magneto/service/impl/DefaultExportService.java @@ -1,5 +1,24 @@ package fr.cgi.magneto.service.impl; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.xslf.usermodel.XMLSlideShow; +import org.apache.poi.xslf.usermodel.XSLFSlide; +import org.entcore.common.user.UserInfos; + import fr.cgi.magneto.core.constants.Field; import fr.cgi.magneto.core.constants.Slideshow; import fr.cgi.magneto.core.enums.SlideResourceType; @@ -21,10 +40,7 @@ import io.vertx.core.logging.LoggerFactory; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.xslf.usermodel.*; -import org.entcore.common.user.UserInfos; - import java.util.*; -import java.util.stream.Collectors; public class DefaultExportService implements ExportService { @@ -57,7 +73,8 @@ public Future exportBoardToPPTX(String boardId, UserInfos user, I1 documentIds.add(imageId); return getBoardDocuments(documentIds); }) - .compose(documents -> createFreeLayoutSlideObjects(board, user, slideShow, documents, i18nHelper)) + .compose(documents -> createFreeLayoutSlideObjects(board, user, slideShow, documents, + i18nHelper)) .onFailure(err -> { String message = String.format( "[Magneto@%s::exportBoardToPptx] Failed to get documents: %s", @@ -112,19 +129,14 @@ private Future fetchDocumentFile(String documentId, Listfuture(promise -> { serviceFactory.storage().readFile(fileId, buffer -> { if (buffer != null) { Map docInfo = new HashMap<>(); docInfo.put(Field.DOCUMENTID, documentId); docInfo.put(Field.BUFFER, buffer); - docInfo.put(Field.EXTENSION, fileExtension); + docInfo.put("contentType", metadata.getString("content-type", "")); documents.add(docInfo); promise.complete(); } else { @@ -141,7 +153,7 @@ private Future fetchDocumentFile(String documentId, List createFreeLayoutSlideObjects(Board board, UserInfos user, - JsonObject slideShowData, List> documents, I18nHelper i18nHelper) { + JsonObject slideShowData, List> documents, I18nHelper i18nHelper) { XMLSlideShow ppt = new XMLSlideShow(); ppt.setPageSize(new java.awt.Dimension(1280, 720)); @@ -154,8 +166,8 @@ private Future createFreeLayoutSlideObjects(Board board, UserInfos SlideFactory slideFactory = new SlideFactory(); // TITRE - XSLFSlide titleApacheSlide = createTitleSlide(board, documents, i18nHelper); - ppt.createSlide().importContent(titleApacheSlide); + XSLFSlide newTitleSlide = ppt.createSlide(); + createTitleSlide(newTitleSlide, board, documents, i18nHelper); // Utiliser l'ordre des cartes du Board for (Card boardCard : board.cards()) { @@ -164,8 +176,13 @@ private Future createFreeLayoutSlideObjects(Board board, UserInfos if (card != null) { try { Slide slide = createSlideFromCard(card, slideFactory, slideShowData, documents); - XSLFSlide apacheSlide = (XSLFSlide) slide.createApacheSlide(); - ppt.createSlide().importContent(apacheSlide); + XSLFSlide newSlide = ppt.createSlide(); + slide.createApacheSlide(newSlide); + // Inspecter le contenu du package après l'ajout de la diapositive + log.info("Package parts after adding slide:"); + for (PackagePart part : ppt.getPackage().getParts()) { + log.info("- " + part.getPartName()); + } } catch (Exception e) { String message = String.format( "[Magneto@%s::createFreeLayoutSlideObjects] Failed to create slide for card %s: %s", @@ -186,13 +203,13 @@ private Future createFreeLayoutSlideObjects(Board board, UserInfos }); } - private XSLFSlide createTitleSlide(Board board, List> documents, I18nHelper i18nHelper) { - XMLSlideShow ppt = new XMLSlideShow(); - XSLFSlide slide = ppt.createSlide(); + private XSLFSlide createTitleSlide(XSLFSlide newTitleSlide, Board board, List> documents, + I18nHelper i18nHelper) { - SlideHelper.createTitle(slide, board.getTitle(), Slideshow.MAIN_TITLE_HEIGHT, Slideshow.MAIN_TITLE_FONT_SIZE, TextParagraph.TextAlign.CENTER); + SlideHelper.createTitle(newTitleSlide, board.getTitle(), Slideshow.MAIN_TITLE_HEIGHT, + Slideshow.MAIN_TITLE_FONT_SIZE, TextParagraph.TextAlign.CENTER); - XSLFTextBox textBox = SlideHelper.createContent(slide); + XSLFTextBox textBox = SlideHelper.createContent(newTitleSlide); XSLFTextParagraph paragraph = textBox.addNewTextParagraph(); paragraph.setTextAlign(TextParagraph.TextAlign.CENTER); @@ -216,14 +233,15 @@ private XSLFSlide createTitleSlide(Board board, List> docume Buffer documentBuffer = (Buffer) documentData.get(Field.BUFFER); String fileExtension = (String) documentData.get(Field.EXTENSION); if (documentBuffer != null) { - SlideHelper.createImage(slide, documentBuffer.getBytes(), fileExtension, Slideshow.MAIN_CONTENT_MARGIN_TOP, Slideshow.MAIN_IMAGE_CONTENT_HEIGHT); + SlideHelper.createImage(newTitleSlide, documentBuffer.getBytes(), fileExtension, + Slideshow.MAIN_CONTENT_MARGIN_TOP, Slideshow.MAIN_IMAGE_CONTENT_HEIGHT); } } - return slide; + return newTitleSlide; } private Slide createSlideFromCard(Card card, SlideFactory slideFactory, JsonObject slideShowData, - List> documents) { + List> documents) { SlideProperties.Builder propertiesBuilder = new SlideProperties.Builder() .title(card.getTitle()) .description(card.getDescription()); @@ -254,10 +272,16 @@ private Slide createSlideFromCard(Card card, SlideFactory slideFactory, JsonObje } Map documentData = documentMap.get(card.getResourceId()); + System.out.println("Document data found for resource ID " + card.getResourceId() + ": " + + (documentData != null ? "yes" : "no")); + Buffer documentBuffer = documentData != null ? (Buffer) documentData.get("buffer") : null; - String fileExtension = documentData != null ? (String) documentData.get("extension") : ""; + String contentType = documentData != null ? (String) documentData.get("contentType") : ""; + + System.out.println("Final contentType: " + contentType); + System.out.println("Buffer present: " + (documentBuffer != null)); propertiesBuilder - .extension(fileExtension) + .contentType(contentType) .resourceData(documentBuffer != null ? documentBuffer.getBytes() : null) .caption(card.getCaption()); break; diff --git a/backend/src/main/resources/img/audio_icon.svg b/backend/src/main/resources/img/audio_icon.svg new file mode 100644 index 00000000..7a5ac734 --- /dev/null +++ b/backend/src/main/resources/img/audio_icon.svg @@ -0,0 +1,6 @@ + + + + + +