Gestionar comentarios de presentación en Node.js
Descripción general
Este artículo explica cómo gestionar los comentarios de una presentación con Aspose.Slides for Node.js via Java. Presenta los tipos principales relacionados con los comentarios y muestra cómo añadir comentarios a las diapositivas, acceder a los comentarios existentes, trabajar con respuestas y comentarios modernos, y eliminar los comentarios de una presentación.
Los ejemplos cubren escenarios comunes de revisión y colaboración en PowerPoint, como asignar comentarios a autores, leer el texto y los metadatos de los comentarios, construir cadenas de respuestas y eliminar comentarios seleccionados o todos los comentarios.
En PowerPoint, los comentarios aparecen como anotaciones en las diapositivas. Al seleccionar un comentario se muestra su texto y la discusión relacionada.
Para solicitar que los comentarios se muestren u oculten al abrir una presentación sin modificar los propios comentarios, consulte Mostrar u ocultar comentarios al abrir una presentación.
¿Por qué añadir comentarios a las presentaciones?
Puedes usar los comentarios para proporcionar retroalimentación y colaborar con colegas al revisar presentaciones.
Aspose.Slides for Node.js via Java ofrece las siguientes API para trabajar con comentarios:
- La clase Presentation que brinda acceso a los autores de comentarios de la presentación.
- La clase CommentCollection que representa los comentarios asociados a un autor individual.
- La clase Comment que proporciona información sobre un comentario, incluido su autor, hora de creación, posición y texto.
- La clase CommentAuthor que proporciona información sobre un autor, incluido su nombre, iniciales y los comentarios asociados.
Añadir comentarios a diapositivas
El ejemplo crea dos diapositivas y registra un autor de comentarios con addAuthor. Usa addComment para colocar un comentario en cada diapositiva, luego recupera los comentarios de la primera diapositiva para ese autor con getSlideComments. La consola muestra el texto del primer comentario dos veces, demostrando el acceso tanto a través de la diapositiva como del autor, y la presentación guardada contiene ambos comentarios.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const presentation = new aspose.slides.Presentation();
try {
const firstSlide = presentation.getSlides().get_Item(0);
const secondSlide = presentation.getSlides().addEmptySlide(presentation.getLayoutSlides().get_Item(0));
const author = presentation.getCommentAuthors().addAuthor("Jawad", "MF");
const position = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(0.2), java.newFloat(0.2));
const createdTime = java.newInstanceSync("java.util.Date");
author.getComments().addComment("Hello Jawad, this is a slide comment", firstSlide, position, createdTime);
author.getComments().addComment("Hello Jawad, this is the second slide comment", secondSlide, position, createdTime);
const comments = firstSlide.getSlideComments(author);
if (comments.length > 0) {
const firstComment = comments[0];
console.log(firstComment.getText());
const commentText = firstComment.getAuthor().getComments().get_Item(0).getText();
console.log(commentText);
}
presentation.save("Comments_out.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Buscar o crear un autor de comentario
Cuando una revisión automatizada procesa la misma presentación repetidamente, busque en la colección devuelta por getCommentAuthors antes de añadir un autor. findByNameAndInitials coincide tanto con el nombre como con las iniciales y devuelve una matriz de autores coincidentes, o una matriz vacía si no existen coincidencias. En contraste, findByName busca solo por nombre, independientemente de las iniciales. Llamar a addAuthor con un nombre e iniciales ya presentes en la colección lanza una excepción.
El ejemplo requiere un archivo de presentación con al menos una diapositiva. En cada una de dos pasadas, reutiliza el primer autor coincidente, o crea uno solo cuando el resultado está vacío. Si hay varias coincidencias, selecciona el primer elemento de la matriz sin combinar o eliminar los demás autores. Luego llama a addComment para el autor seleccionado y guarda la presentación. La segunda pasada abre la salida de la primera pasada y repite la operación.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const authorName = "Reviewer";
const authorInitials = "RV";
let inputFile = "input.pptx";
const position = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(20), java.newFloat(20));
for (let pass = 1; pass <= 2; pass++) {
const presentation = new aspose.slides.Presentation(inputFile);
try {
const matches = presentation.getCommentAuthors().findByNameAndInitials(authorName, authorInitials);
const author = matches.length > 0 ? matches[0] : presentation.getCommentAuthors().addAuthor(authorName, authorInitials);
const createdTime = java.newInstanceSync("java.util.Date");
author.getComments().addComment("Please review this slide.", presentation.getSlides().get_Item(0), position, createdTime);
const outputFile = "review_pass_" + pass + ".pptx";
presentation.save(outputFile, aspose.slides.SaveFormat.Pptx);
inputFile = outputFile;
} finally {
presentation.dispose();
}
}
La segunda pasada añade otro comentario sin añadir otro autor ni eliminar los comentarios existentes. El archivo final, review_pass_2.pptx, contiene ambos comentarios nuevos junto a cualquier comentario original. Reutilizar un autor no deduplica los comentarios: para evitar añadir repetidamente el mismo comentario, implemente una detección de duplicados independiente antes de añadirlo.
Acceder a los comentarios de las diapositivas
El ejemplo abre una presentación existente y lee la colección devuelta por getCommentAuthors. Para cada autor, itera a través de la colección devuelta por getComments e imprime el número de diapositiva, el texto del comentario, el nombre del autor y la hora de creación. La presentación de entrada debe contener comentarios para producir salida en la consola.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const presentation = new aspose.slides.Presentation("Comments1.pptx");
try {
const authors = presentation.getCommentAuthors();
for (let authorIndex = 0; authorIndex < authors.size(); authorIndex++) {
const author = authors.get_Item(authorIndex);
const comments = author.getComments();
for (let commentIndex = 0; commentIndex < comments.size(); commentIndex++) {
const comment = comments.get_Item(commentIndex);
console.log("Slide: " + comment.getSlide().getSlideNumber());
console.log("Comment: " + comment.getText());
console.log("Author: " + comment.getAuthor().getName());
console.log("Posted at: " + comment.getCreatedTime());
console.log();
}
}
} finally {
presentation.dispose();
}
Responder a los comentarios
Un comentario padre es el comentario original en la parte superior de una jerarquía de respuestas. Los métodos Comment.getParentComment y Comment.setParentComment permiten obtener o establecer el padre de un comentario.
El ejemplo crea comentarios de dos autores y usa setParentComment para construir respuestas, incluida una respuesta a otra respuesta. Pasar null a getSlideComments recupera los comentarios de todos los autores; la consola sangra cada entrada según su profundidad en la jerarquía de respuestas. El ejemplo guarda la discusión completa, llama a remove sobre el primer comentario raíz y guarda una segunda presentación con ese comentario y sus respuestas eliminados.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const presentation = new aspose.slides.Presentation();
try {
const slide = presentation.getSlides().get_Item(0);
const position = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(10), java.newFloat(10));
const createdTime = java.newInstanceSync("java.util.Date");
const originalAuthor = presentation.getCommentAuthors().addAuthor("Author_1", "A.A.");
const rootComment = originalAuthor.getComments().addComment("comment 1", slide, position, createdTime);
const reviewer = presentation.getCommentAuthors().addAuthor("Author_2", "B.B.");
const firstReply = reviewer.getComments().addComment("reply 1 for comment 1", slide, position, createdTime);
firstReply.setParentComment(rootComment);
const secondReply = reviewer.getComments().addComment("reply 2 for comment 1", slide, position, createdTime);
secondReply.setParentComment(rootComment);
const subReply = originalAuthor.getComments().addComment("subreply 3 for reply 2", slide, position, createdTime);
subReply.setParentComment(secondReply);
reviewer.getComments().addComment("comment 2", slide, position, createdTime);
const separateComment = reviewer.getComments().addComment("comment 3", slide, position, createdTime);
const separateReply = originalAuthor.getComments().addComment("reply 4 for comment 3", slide, position, createdTime);
separateReply.setParentComment(separateComment);
const comments = slide.getSlideComments(null);
for (let index = 0; index < comments.length; index++) {
let comment = comments[index];
let indentation = "";
while (comment.getParentComment() != null) {
indentation += "\t";
comment = comment.getParentComment();
}
console.log(indentation + comments[index].getAuthor().getName() + ": " + comments[index].getText());
}
presentation.save("parent_comment.pptx", aspose.slides.SaveFormat.Pptx);
rootComment.remove();
presentation.save("remove_comment.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Warning
- Cuando se usa el método Comment.remove para eliminar un comentario, también se eliminan todas las respuestas a ese comentario.
- Si Comment.setParentComment crea una referencia circular, se lanza una PptxEditException.
Añadir comentarios modernos
Los comentarios modernos pueden asociarse a la propia diapositiva, a una forma específica o a un rango de texto dentro de un AutoShape. El método CommentCollection.addModernComment acepta un argumento Shape además de la diapositiva y las coordenadas del marcador del comentario.
Cuando se pasa null para el argumento de forma, el comentario es un comentario a nivel de diapositiva. Su marcador se posiciona mediante las coordenadas suministradas, pero no está asociado a una forma concreta, por lo que ModernComment.getShape devuelve null. Cuando se proporciona una Shape, el comentario se ancla a esa forma. Las coordenadas siguen definiendo la posición del marcador del comentario en la diapositiva, mientras que la asociación de forma puede recuperarse mediante ModernComment.getShape.
Anclar un comentario moderno a una forma
El ejemplo llama a addModernComment dos veces: una vez con una forma null para un comentario a nivel de diapositiva y una vez con un rectángulo como ancla. Leer getShape imprime true para la comprobación de nulidad del comentario a nivel de diapositiva y Revenue title para el comentario anclado. Ambos comentarios se guardan con la presentación.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const presentation = new aspose.slides.Presentation();
try {
const slide = presentation.getSlides().get_Item(0);
const author = presentation.getCommentAuthors().addAuthor("Reviewer", "RV");
const shape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 50, 50, 300, 80);
shape.setName("Revenue title");
shape.getTextFrame().setText("Quarterly revenue");
const createdTime = java.newInstanceSync("java.util.Date");
const slideCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(20), java.newFloat(20));
const shapeCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(60), java.newFloat(60));
const slideComment = author.getComments().addModernComment("Review the overall slide layout.", slide, null, slideCommentPosition, createdTime);
const shapeComment = author.getComments().addModernComment("Check this title.", slide, shape, shapeCommentPosition, createdTime);
console.log(slideComment.getShape() == null);
console.log(shapeComment.getShape().getName());
presentation.save("modern_comments.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Anclar comentarios a diferentes tipos de forma
Cualquier objeto de diapositiva derivado de Shape puede usarse como ancla de forma. Los ejemplos más comunes incluyen AutoShape, PictureFrame, GroupShape, Connector y GraphicalObject como gráficos.
El ejemplo utiliza addModernComment para asociar un comentario con cada uno de cinco tipos de forma: un AutoShape, un picture frame, un grupo, un conector y un gráfico. La imagen usa datos de imagen incrustados, por lo que no se requiere una imagen externa. Cada comentario tiene su propia posición de marcador, y la presentación guardada conserva las cinco asociaciones de forma.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const presentation = new aspose.slides.Presentation();
try {
const slide = presentation.getSlides().get_Item(0);
const author = presentation.getCommentAuthors().addAuthor("Reviewer", "RV");
const createdTime = java.newInstanceSync("java.util.Date");
const autoShape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 20, 20, 180, 60);
autoShape.getTextFrame().setText("AutoShape");
const autoShapeCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(30), java.newFloat(30));
author.getComments().addModernComment("Comment on an AutoShape.", slide, autoShape, autoShapeCommentPosition, createdTime);
const imageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVR4nGP8//8/AwMDEwMDAwMDAwAkBgMB/DXemwAAAABJRU5ErkJggg==";
const imageData = java.newArray("byte", Array.from(Buffer.from(imageBase64, "base64")));
const image = presentation.getImages().addImage(imageData);
const pictureFrame = slide.getShapes().addPictureFrame(aspose.slides.ShapeType.Rectangle, 220, 20, 120, 80, image);
const pictureCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(230), java.newFloat(30));
author.getComments().addModernComment("Comment on a picture.", slide, pictureFrame, pictureCommentPosition, createdTime);
const groupShape = slide.getShapes().addGroupShape();
groupShape.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 0, 0, 80, 40);
groupShape.getShapes().addAutoShape(aspose.slides.ShapeType.Ellipse, 100, 0, 80, 40);
const groupCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(40), java.newFloat(150));
author.getComments().addModernComment("Comment on a group.", slide, groupShape, groupCommentPosition, createdTime);
const connector = slide.getShapes().addConnector(aspose.slides.ShapeType.StraightConnector1, 220, 150, 140, 40);
const connectorCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(240), java.newFloat(150));
author.getComments().addModernComment("Comment on a connector.", slide, connector, connectorCommentPosition, createdTime);
const chart = slide.getShapes().addChart(aspose.slides.ChartType.ClusteredColumn, 400, 20, 250, 180);
const chartCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(420), java.newFloat(40));
author.getComments().addModernComment("Comment on a graphical object.", slide, chart, chartCommentPosition, createdTime);
presentation.save("modern_comment_shape_types.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Anclar un comentario a texto y establecer su estado
Para un comentario moderno asociado a un AutoShape, ModernComment.getTextSelectionStart y ModernComment.setTextSelectionStart acceden a la posición inicial del texto seleccionado en el marco de texto de la forma. ModernComment.getTextSelectionLength y ModernComment.setTextSelectionLength acceden a la longitud de la selección. Juntos, estos valores asocian el comentario con un rango de texto específico dentro del AutoShape.
Los métodos ModernComment.getStatus y ModernComment.setStatus acceden a un valor de la enumeración ModernCommentStatus:
NotDefined— no se ha definido ningún estado específico de comentario moderno.Active— el comentario está activo.Resolved— el comentario ha sido resuelto.Closed— el comentario está cerrado.
El ejemplo ancla un comentario moderno a las palabras “quarterly revenue” en el texto de un rectángulo. Calcula el inicio de selección basado en cero a partir del texto y lo pasa a setTextSelectionStart y usa setTextSelectionLength para establecer el número de caracteres seleccionados. Después de llamar a setStatus con Resolved, guarda y vuelve a abrir la presentación. Las cuatro comprobaciones en consola deben imprimir true, confirmando que el ancla de forma, el rango de texto y el estado se conservaron.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const outputFile = "modern_comment_text_anchor.pptx";
const shapeText = "Review the quarterly revenue forecast.";
const selectedText = "quarterly revenue";
const expectedSelectionStart = shapeText.indexOf(selectedText);
const presentation = new aspose.slides.Presentation();
try {
const slide = presentation.getSlides().get_Item(0);
const shape = slide.getShapes().addAutoShape(aspose.slides.ShapeType.Rectangle, 50, 50, 400, 100);
shape.setName("Forecast text");
shape.getTextFrame().setText(shapeText);
const author = presentation.getCommentAuthors().addAuthor("Reviewer", "RV");
const commentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(60), java.newFloat(60));
const createdTime = java.newInstanceSync("java.util.Date");
const comment = author.getComments().addModernComment("Verify this forecast wording.", slide, shape, commentPosition, createdTime);
comment.setTextSelectionStart(expectedSelectionStart);
comment.setTextSelectionLength(selectedText.length);
comment.setStatus(java.newByte(aspose.slides.ModernCommentStatus.Resolved));
presentation.save(outputFile, aspose.slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
const reopenedPresentation = new aspose.slides.Presentation(outputFile);
try {
const reopenedSlide = reopenedPresentation.getSlides().get_Item(0);
const reopenedComments = reopenedSlide.getSlideComments(null);
for (let index = 0; index < reopenedComments.length; index++) {
const reopenedComment = reopenedComments[index];
if (!java.instanceOf(reopenedComment, "com.aspose.slides.IModernComment")) {
continue;
}
const shapeMatches = reopenedComment.getShape() != null && reopenedComment.getShape().getName() === "Forecast text";
const selectionStartMatches = reopenedComment.getTextSelectionStart() === expectedSelectionStart;
const selectionLengthMatches = reopenedComment.getTextSelectionLength() === selectedText.length;
const statusMatches = reopenedComment.getStatus() === aspose.slides.ModernCommentStatus.Resolved;
console.log("Shape anchor preserved: " + shapeMatches);
console.log("Text selection start preserved: " + selectionStartMatches);
console.log("Text selection length preserved: " + selectionLengthMatches);
console.log("Resolved status preserved: " + statusMatches);
}
} finally {
reopenedPresentation.dispose();
}
Inspeccionar comentarios modernos existentes
El ejemplo requiere una presentación existente que contenga comentarios modernos e imprime su texto, estado y detalles del ancla. Para inspeccionarlo, verifique qué comentarios son instancias de ModernComment, luego examine ModernComment.getShape, ModernComment.getTextSelectionStart, ModernComment.getTextSelectionLength y ModernComment.getStatus. Una forma null indica un comentario a nivel de diapositiva. Para un ancla de AutoShape, los métodos de selección de texto identifican el rango asociado en el marco de texto de la forma.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const presentation = new aspose.slides.Presentation("comments.pptx");
try {
const slides = presentation.getSlides();
for (let slideIndex = 0; slideIndex < slides.size(); slideIndex++) {
const slide = slides.get_Item(slideIndex);
const comments = slide.getSlideComments(null);
for (let commentIndex = 0; commentIndex < comments.length; commentIndex++) {
const comment = comments[commentIndex];
if (!java.instanceOf(comment, "com.aspose.slides.IModernComment")) {
continue;
}
console.log("Slide: " + slide.getSlideNumber());
console.log("Text: " + comment.getText());
console.log("Status: " + comment.getStatus());
const shape = comment.getShape();
if (shape == null) {
console.log("Anchor: slide level");
} else {
console.log("Anchor shape: " + shape.getName());
console.log("Anchor type: " + shape.getClass().getSimpleName());
if (java.instanceOf(shape, "com.aspose.slides.IAutoShape")) {
console.log("Text selection start: " + comment.getTextSelectionStart());
console.log("Text selection length: " + comment.getTextSelectionLength());
}
}
console.log();
}
}
} finally {
presentation.dispose();
}
Eliminar comentarios
Aspose.Slides for Node.js via Java permite eliminar todos los comentarios y sus autores de una presentación o borrar comentarios seleccionados manteniendo el resto. Los siguientes ejemplos demuestran ambos enfoques y guardan la presentación actualizada.
Eliminar todos los comentarios y autores de comentarios
El ejemplo abre una presentación existente y llama a CommentCollection.clear para cada autor, luego llama a CommentAuthorCollection.clear para eliminar a los autores mismos. La presentación guardada no contiene ni comentarios ni autores de comentarios. Use una presentación que contenga comentarios para observar la eliminación.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const presentation = new aspose.slides.Presentation("example.pptx");
try {
const authors = presentation.getCommentAuthors();
for (let index = 0; index < authors.size(); index++) {
authors.get_Item(index).getComments().clear();
}
authors.clear();
presentation.save("example_out.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Eliminar comentarios específicos
El ejemplo crea dos comentarios en una diapositiva y usa getSlideComments para encontrar los comentarios de cada autor. Recopila los comentarios cuyo texto es igual a “comment 1” antes de llamar a CommentCollection.remove, evitando cambios en la colección durante la selección. La presentación guardada conserva solo “comment 2”.
var aspose = aspose || {};
aspose.slides = require("aspose.slides.via.java");
const java = require("java");
const presentation = new aspose.slides.Presentation();
try {
const slide = presentation.getSlides().get_Item(0);
const author = presentation.getCommentAuthors().addAuthor("Author", "A");
const createdTime = java.newInstanceSync("java.util.Date");
const firstCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(0.2), java.newFloat(0.2));
const secondCommentPosition = java.newInstanceSync("com.aspose.slides.Point2DFloat", java.newFloat(0.3), java.newFloat(0.2));
author.getComments().addComment("comment 1", slide, firstCommentPosition, createdTime);
author.getComments().addComment("comment 2", slide, secondCommentPosition, createdTime);
const authors = presentation.getCommentAuthors();
for (let authorIndex = 0; authorIndex < authors.size(); authorIndex++) {
const commentAuthor = authors.get_Item(authorIndex);
const commentsToRemove = [];
const comments = slide.getSlideComments(commentAuthor);
for (let commentIndex = 0; commentIndex < comments.length; commentIndex++) {
const comment = comments[commentIndex];
if (comment.getText() === "comment 1") {
commentsToRemove.push(comment);
}
}
for (const comment of commentsToRemove) {
commentAuthor.getComments().remove(comment);
}
}
presentation.save("pres.pptx", aspose.slides.SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Preguntas frecuentes
¿Aspose.Slides admite un estado resuelto para los comentarios modernos?
Sí. ModernComment.getStatus y ModernComment.setStatus acceden a un valor de ModernCommentStatus, incluido Resolved. El estado se almacena en la presentación y puede leerse nuevamente después de volver a abrir el archivo.
¿Se admiten discusiones en hilos (cadenas de respuestas) y existe un límite de anidamiento?
Sí. Cada comentario puede hacer referencia a su parent comment, lo que permite cadenas de respuestas. La API no define un límite específico de profundidad de anidamiento.
¿En qué sistema de coordenadas se define la posición del marcador de un comentario en una diapositiva?
La posición del marcador se define mediante coordenadas de punto flotante en el sistema de coordenadas de la diapositiva, lo que permite colocarlo con precisión en la diapositiva.