Gerenciar Comentários de Apresentação em Java
Visão geral
Este artigo explica como gerenciar comentários de apresentação com Aspose.Slides para Java. Ele apresenta os principais tipos relacionados a comentários e demonstra como adicionar comentários a slides, acessar comentários existentes, trabalhar com respostas e comentários modernos, e remover comentários de uma apresentação.
Os exemplos cobrem cenários comuns de revisão e colaboração no PowerPoint, como atribuir comentários a autores, ler o texto e os metadados dos comentários, construir cadeias de respostas e remover comentários selecionados ou todos os comentários.
No PowerPoint, os comentários aparecem como anotações nos slides. Selecionar um comentário exibe seu texto e a discussão relacionada.
Para solicitar que os comentários sejam mostrados ou ocultados ao abrir uma apresentação sem alterar os próprios comentários, veja Exibir ou Ocultar Comentários ao Abrir uma Apresentação.
Por que Adicionar Comentários a Apresentações?
Você pode usar comentários para fornecer feedback e colaborar com colegas ao revisar apresentações.
Aspose.Slides para Java fornece as seguintes APIs para trabalhar com comentários:
- A classe Presentation que fornece acesso aos autores de comentários da apresentação.
- A interface ICommentCollection que representa os comentários associados a um autor individual.
- A interface IComment que fornece informações sobre um comentário, incluindo seu autor, horário de criação, posição e texto.
- A classe CommentAuthor que fornece informações sobre um autor, incluindo seu nome, iniciais e comentários associados.
Adicionar Comentários ao Slide
O exemplo cria dois slides e registra um autor de comentário com addAuthor. Ele usa addComment para colocar um comentário em cada slide, então recupera os comentários do primeiro slide para esse autor com getSlideComments. O console exibe o texto do primeiro comentário duas vezes, demonstrando o acesso tanto pelo slide quanto pelo autor, e a apresentação salva contém ambos os comentários.
import com.aspose.slides.IComment;
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.ISlide;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import java.awt.geom.Point2D;
import java.util.Date;
Presentation presentation = new Presentation();
try {
ISlide firstSlide = presentation.getSlides().get_Item(0);
ISlide secondSlide = presentation.getSlides().addEmptySlide(presentation.getLayoutSlides().get_Item(0));
ICommentAuthor author = presentation.getCommentAuthors().addAuthor("Jawad", "MF");
Point2D.Float position = new Point2D.Float(0.2f, 0.2f);
Date createdTime = new 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);
IComment[] comments = firstSlide.getSlideComments(author);
if (comments.length > 0) {
IComment firstComment = comments[0];
System.out.println(firstComment.getText());
String commentText = firstComment.getAuthor().getComments().get_Item(0).getText();
System.out.println(commentText);
}
presentation.save("Comments_out.pptx", SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Localizar ou Criar um Autor de Comentário
Quando uma revisão automatizada processa a mesma apresentação repetidamente, procure na coleção retornada por getCommentAuthors antes de adicionar um autor. findByNameAndInitials corresponde tanto ao nome quanto às iniciais e retorna um array de autores correspondentes, ou um array vazio se não houver correspondências. Em contraste, findByName pesquisa apenas pelo nome, independentemente das iniciais. Chamando addAuthor com um nome e iniciais já presentes na coleção gera uma exceção.
O exemplo requer um arquivo de apresentação com pelo menos um slide. Em cada uma das duas passagens, ele reutiliza o primeiro autor correspondente, ou cria um apenas quando o resultado está vazio. Se houver múltiplas correspondências, ele seleciona o primeiro elemento do array sem mesclar ou remover os outros autores. Em seguida, ele chama addComment para o autor selecionado e salva a apresentação. A segunda passagem abre a saída da primeira passagem e repete a operação.
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import java.awt.geom.Point2D;
import java.util.Date;
final String authorName = "Reviewer";
final String authorInitials = "RV";
String inputFile = "input.pptx";
Point2D.Float position = new Point2D.Float(20, 20);
for (int pass = 1; pass <= 2; pass++) {
Presentation presentation = new Presentation(inputFile);
try {
ICommentAuthor[] matches = presentation.getCommentAuthors().findByNameAndInitials(authorName, authorInitials);
ICommentAuthor author = matches.length > 0 ? matches[0] : presentation.getCommentAuthors().addAuthor(authorName, authorInitials);
author.getComments().addComment("Please review this slide.", presentation.getSlides().get_Item(0), position, new Date());
String outputFile = "review_pass_" + pass + ".pptx";
presentation.save(outputFile, SaveFormat.Pptx);
inputFile = outputFile;
} finally {
presentation.dispose();
}
}
A segunda passagem adiciona outro comentário sem adicionar outro autor nem remover comentários existentes. O arquivo final, review_pass_2.pptx, contém ambos os novos comentários juntamente com quaisquer comentários originais. Reusar um autor não deduplica comentários: para evitar adicionar repetidamente o mesmo comentário, implemente detecção de duplicatas separada antes de adicioná‑lo.
Acessar Comentários do Slide
O exemplo abre uma apresentação existente e lê a coleção retornada por getCommentAuthors. Para cada autor, ele itera pela coleção retornada por getComments e imprime o número do slide, o texto do comentário, o nome do autor e o horário de criação. A apresentação de entrada deve conter comentários para produzir saída no console.
import com.aspose.slides.IComment;
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.Presentation;
Presentation presentation = new Presentation("Comments1.pptx");
try {
for (ICommentAuthor author : presentation.getCommentAuthors()) {
for (IComment comment : author.getComments()) {
System.out.println("Slide: " + comment.getSlide().getSlideNumber());
System.out.println("Comment: " + comment.getText());
System.out.println("Author: " + comment.getAuthor().getName());
System.out.println("Posted at: " + comment.getCreatedTime());
System.out.println();
}
}
} finally {
presentation.dispose();
}
Responder a Comentários
Um comentário pai é o comentário original no topo de uma hierarquia de respostas. Os métodos IComment.getParentComment e IComment.setParentComment permitem obter ou definir o pai de um comentário.
O exemplo cria comentários de dois autores e usa setParentComment para construir respostas, incluindo uma resposta a outra resposta. Passar null para getSlideComments recupera comentários de todos os autores; o console indentifica cada entrada de acordo com sua profundidade na hierarquia de respostas. O exemplo salva a discussão completa, chama remove no primeiro comentário raiz e salva uma segunda apresentação com esse comentário e suas respostas removidos.
import com.aspose.slides.IComment;
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.ISlide;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import java.awt.geom.Point2D;
import java.util.Date;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
Point2D.Float position = new Point2D.Float(10, 10);
Date createdTime = new Date();
ICommentAuthor originalAuthor = presentation.getCommentAuthors().addAuthor("Author_1", "A.A.");
IComment rootComment = originalAuthor.getComments().addComment("comment 1", slide, position, createdTime);
ICommentAuthor reviewer = presentation.getCommentAuthors().addAuthor("Author_2", "B.B.");
IComment firstReply = reviewer.getComments().addComment("reply 1 for comment 1", slide, position, createdTime);
firstReply.setParentComment(rootComment);
IComment secondReply = reviewer.getComments().addComment("reply 2 for comment 1", slide, position, createdTime);
secondReply.setParentComment(rootComment);
IComment subReply = originalAuthor.getComments().addComment("subreply 3 for reply 2", slide, position, createdTime);
subReply.setParentComment(secondReply);
reviewer.getComments().addComment("comment 2", slide, position, createdTime);
IComment separateComment = reviewer.getComments().addComment("comment 3", slide, position, createdTime);
IComment separateReply = originalAuthor.getComments().addComment("reply 4 for comment 3", slide, position, createdTime);
separateReply.setParentComment(separateComment);
IComment[] comments = slide.getSlideComments(null);
for (int i = 0; i < comments.length; i++) {
IComment comment = comments[i];
while (comment.getParentComment() != null) {
System.out.print("\t");
comment = comment.getParentComment();
}
System.out.println(comments[i].getAuthor().getName() + ": " + comments[i].getText());
}
presentation.save("parent_comment.pptx", SaveFormat.Pptx);
rootComment.remove();
presentation.save("remove_comment.pptx", SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Warning
- Quando o método IComment.remove é usado para excluir um comentário, todas as respostas a esse comentário também são excluídas.
- Se IComment.setParentComment criar uma referência circular, uma PptxEditException será lançada.
Adicionar Comentários Modernos
Comentários modernos podem ser associados ao próprio slide, a uma forma específica ou a um intervalo de texto dentro de uma AutoShape. O método ICommentCollection.addModernComment aceita um argumento IShape além do slide e das coordenadas do marcador de comentário.
Quando null é passado para o argumento shape, o comentário é um comentário ao nível do slide. Seu marcador é posicionado pelas coordenadas fornecidas, mas não está associado a uma forma específica, portanto IModernComment.getShape retorna null. Quando um IShape é fornecido, o comentário fica ancorado a essa forma. As coordenadas ainda definem a posição do marcador de comentário no slide, enquanto a associação à forma pode ser obtida através de IModernComment.getShape.
Ancorar um Comentário Moderno a uma Forma
O exemplo chama addModernComment duas vezes: uma vez com shape null para um comentário ao nível do slide e outra com um retângulo como ancora. Ler getShape imprime true para a verificação nula do comentário ao nível do slide e Revenue title para o comentário ancorado. Ambos os comentários são salvos com a apresentação.
import com.aspose.slides.IAutoShape;
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.IModernComment;
import com.aspose.slides.ISlide;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import com.aspose.slides.ShapeType;
import java.awt.geom.Point2D;
import java.util.Date;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
ICommentAuthor author = presentation.getCommentAuthors().addAuthor("Reviewer", "RV");
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 50, 50, 300, 80);
shape.setName("Revenue title");
shape.getTextFrame().setText("Quarterly revenue");
Date createdTime = new Date();
Point2D.Float slideCommentPosition = new Point2D.Float(20, 20);
Point2D.Float shapeCommentPosition = new Point2D.Float(60, 60);
IModernComment slideComment = author.getComments().addModernComment("Review the overall slide layout.", slide, null, slideCommentPosition, createdTime);
IModernComment shapeComment = author.getComments().addModernComment("Check this title.", slide, shape, shapeCommentPosition, createdTime);
System.out.println(slideComment.getShape() == null);
System.out.println(shapeComment.getShape().getName());
presentation.save("modern_comments.pptx", SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Ancorar Comentários a Diferentes Tipos de Forma
Qualquer objeto de slide que implemente IShape pode ser usado como âncora de forma. Exemplos comuns incluem IAutoShape, IPictureFrame, IGroupShape, IConnector e instâncias de IGraphicalObject como gráficos.
O exemplo usa addModernComment para associar um comentário a cada um de cinco tipos de forma: uma AutoShape, um quadro de imagem, um grupo, um conector e um gráfico. A imagem usa dados incorporados, portanto nenhuma imagem externa é necessária. Cada comentário tem sua própria posição de marcador, e a apresentação salva mantém todas as cinco associações de forma.
import com.aspose.slides.ChartType;
import com.aspose.slides.IAutoShape;
import com.aspose.slides.IChart;
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.IConnector;
import com.aspose.slides.IGroupShape;
import com.aspose.slides.IPPImage;
import com.aspose.slides.IPictureFrame;
import com.aspose.slides.ISlide;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import com.aspose.slides.ShapeType;
import java.awt.geom.Point2D;
import java.util.Base64;
import java.util.Date;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
ICommentAuthor author = presentation.getCommentAuthors().addAuthor("Reviewer", "RV");
Date createdTime = new Date();
IAutoShape autoShape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 20, 20, 180, 60);
autoShape.getTextFrame().setText("AutoShape");
Point2D.Float autoShapeCommentPosition = new Point2D.Float(30, 30);
author.getComments().addModernComment("Comment on an AutoShape.", slide, autoShape, autoShapeCommentPosition, createdTime);
String imageBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAFklEQVR4nGP8//8/AwMDEwMDAwMDAwAkBgMB/DXemwAAAABJRU5ErkJggg==";
byte[] imageData = Base64.getDecoder().decode(imageBase64);
IPPImage image = presentation.getImages().addImage(imageData);
IPictureFrame pictureFrame = slide.getShapes().addPictureFrame(ShapeType.Rectangle, 220, 20, 120, 80, image);
Point2D.Float pictureCommentPosition = new Point2D.Float(230, 30);
author.getComments().addModernComment("Comment on a picture.", slide, pictureFrame, pictureCommentPosition, createdTime);
IGroupShape groupShape = slide.getShapes().addGroupShape();
groupShape.getShapes().addAutoShape(ShapeType.Rectangle, 0, 0, 80, 40);
groupShape.getShapes().addAutoShape(ShapeType.Ellipse, 100, 0, 80, 40);
Point2D.Float groupCommentPosition = new Point2D.Float(40, 150);
author.getComments().addModernComment("Comment on a group.", slide, groupShape, groupCommentPosition, createdTime);
IConnector connector = slide.getShapes().addConnector(ShapeType.StraightConnector1, 220, 150, 140, 40);
Point2D.Float connectorCommentPosition = new Point2D.Float(240, 150);
author.getComments().addModernComment("Comment on a connector.", slide, connector, connectorCommentPosition, createdTime);
IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 400, 20, 250, 180);
Point2D.Float chartCommentPosition = new Point2D.Float(420, 40);
author.getComments().addModernComment("Comment on a graphical object.", slide, chart, chartCommentPosition, createdTime);
presentation.save("modern_comment_shape_types.pptx", SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Ancorar um Comentário ao Texto e Definir Seu Status
Para um comentário moderno associado a uma IAutoShape, os métodos IModernComment.getTextSelectionStart e IModernComment.setTextSelectionStart acessam a posição inicial do texto selecionado na caixa de texto da forma. Os métodos IModernComment.getTextSelectionLength e IModernComment.setTextSelectionLength acessam o comprimento da seleção. Juntos, esses valores associam o comentário a um intervalo de texto específico dentro da AutoShape.
Os métodos IModernComment.getStatus e IModernComment.setStatus acessam um valor dos constantes ModernCommentStatus:
NotDefined— nenhum status de comentário moderno específico está definido.Active— o comentário está ativo.Resolved— o comentário foi resolvido.Closed— o comentário está fechado.
O exemplo ancora um comentário moderno às palavras “quarterly revenue” no texto de um retângulo. Ele calcula o início da seleção baseado em zero a partir do texto e o passa para setTextSelectionStart e usa setTextSelectionLength para definir o número de caracteres selecionados. Depois de chamar setStatus com Resolved, ele salva e reabre a apresentação. As quatro verificações no console devem imprimir true, confirmando que a ancora da forma, o intervalo de texto e o status foram preservados.
import com.aspose.slides.IAutoShape;
import com.aspose.slides.IComment;
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.IModernComment;
import com.aspose.slides.ISlide;
import com.aspose.slides.ModernCommentStatus;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import com.aspose.slides.ShapeType;
import java.awt.geom.Point2D;
import java.util.Date;
String outputFile = "modern_comment_text_anchor.pptx";
String shapeText = "Review the quarterly revenue forecast.";
String selectedText = "quarterly revenue";
int expectedSelectionStart = shapeText.indexOf(selectedText);
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
IAutoShape shape = slide.getShapes().addAutoShape(ShapeType.Rectangle, 50, 50, 400, 100);
shape.setName("Forecast text");
shape.getTextFrame().setText(shapeText);
ICommentAuthor author = presentation.getCommentAuthors().addAuthor("Reviewer", "RV");
Point2D.Float commentPosition = new Point2D.Float(60, 60);
IModernComment comment = author.getComments().addModernComment("Verify this forecast wording.", slide, shape, commentPosition, new Date());
comment.setTextSelectionStart(expectedSelectionStart);
comment.setTextSelectionLength(selectedText.length());
comment.setStatus(ModernCommentStatus.Resolved);
presentation.save(outputFile, SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Presentation reopenedPresentation = new Presentation(outputFile);
try {
ISlide reopenedSlide = reopenedPresentation.getSlides().get_Item(0);
IComment[] reopenedComments = reopenedSlide.getSlideComments(null);
for (IComment reopenedComment : reopenedComments) {
if (!(reopenedComment instanceof IModernComment)) {
continue;
}
IModernComment modernComment = (IModernComment) reopenedComment;
boolean shapeMatches = modernComment.getShape() != null && "Forecast text".equals(modernComment.getShape().getName());
boolean selectionStartMatches = modernComment.getTextSelectionStart() == expectedSelectionStart;
boolean selectionLengthMatches = modernComment.getTextSelectionLength() == selectedText.length();
boolean statusMatches = modernComment.getStatus() == ModernCommentStatus.Resolved;
System.out.println("Shape anchor preserved: " + shapeMatches);
System.out.println("Text selection start preserved: " + selectionStartMatches);
System.out.println("Text selection length preserved: " + selectionLengthMatches);
System.out.println("Resolved status preserved: " + statusMatches);
}
} finally {
reopenedPresentation.dispose();
}
Inspecionar Comentários Modernos Existentes
O exemplo requer uma apresentação existente contendo comentários modernos e imprime seu texto, status e detalhes de ancoragem. Para inspecioná‑los, verifique quais comentários implementam IModernComment, então examine IModernComment.getShape, IModernComment.getTextSelectionStart, IModernComment.getTextSelectionLength e IModernComment.getStatus. Uma forma null indica um comentário ao nível do slide. Para uma ancora de IAutoShape, os métodos de seleção de texto identificam o intervalo associado no quadro de texto da forma.
import com.aspose.slides.IAutoShape;
import com.aspose.slides.IComment;
import com.aspose.slides.IModernComment;
import com.aspose.slides.IShape;
import com.aspose.slides.ISlide;
import com.aspose.slides.Presentation;
Presentation presentation = new Presentation("comments.pptx");
try {
for (ISlide slide : presentation.getSlides()) {
IComment[] comments = slide.getSlideComments(null);
for (IComment comment : comments) {
if (!(comment instanceof IModernComment)) {
continue;
}
IModernComment modernComment = (IModernComment) comment;
System.out.println("Slide: " + slide.getSlideNumber());
System.out.println("Text: " + modernComment.getText());
System.out.println("Status: " + modernComment.getStatus());
IShape shape = modernComment.getShape();
if (shape == null) {
System.out.println("Anchor: slide level");
} else {
System.out.println("Anchor shape: " + shape.getName());
System.out.println("Anchor type: " + shape.getClass().getSimpleName());
if (shape instanceof IAutoShape) {
System.out.println("Text selection start: " + modernComment.getTextSelectionStart());
System.out.println("Text selection length: " + modernComment.getTextSelectionLength());
}
}
System.out.println();
}
}
} finally {
presentation.dispose();
}
Remover Comentários
Aspose.Slides para Java permite remover todos os comentários e seus autores de uma apresentação ou excluir comentários selecionados mantendo os demais. Os exemplos a seguir demonstram ambas as abordagens e salvam a apresentação atualizada.
Remover Todos os Comentários e Autores de Comentários
O exemplo abre uma apresentação existente e chama ICommentCollection.clear para cada autor, depois chama ICommentAuthorCollection.clear para remover os próprios autores. A apresentação salva não contém nem comentários nem autores de comentários. Use uma apresentação que contenha comentários para observar a remoção.
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
Presentation presentation = new Presentation("example.pptx");
try {
for (ICommentAuthor author : presentation.getCommentAuthors()) {
author.getComments().clear();
}
presentation.getCommentAuthors().clear();
presentation.save("example_out.pptx", SaveFormat.Pptx);
} finally {
presentation.dispose();
}
Remover Comentários Específicos
O exemplo cria dois comentários em um slide e usa getSlideComments para encontrar comentários de cada autor. Ele coleta os comentários cujo texto é igual a “comment 1” antes de chamar ICommentCollection.remove, evitando alterações na coleção durante a seleção. A apresentação salva retém apenas “comment 2”.
import com.aspose.slides.IComment;
import com.aspose.slides.ICommentAuthor;
import com.aspose.slides.ISlide;
import com.aspose.slides.Presentation;
import com.aspose.slides.SaveFormat;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
Presentation presentation = new Presentation();
try {
ISlide slide = presentation.getSlides().get_Item(0);
ICommentAuthor author = presentation.getCommentAuthors().addAuthor("Author", "A");
Date createdTime = new Date();
Point2D.Float firstCommentPosition = new Point2D.Float(0.2f, 0.2f);
Point2D.Float secondCommentPosition = new Point2D.Float(0.3f, 0.2f);
author.getComments().addComment("comment 1", slide, firstCommentPosition, createdTime);
author.getComments().addComment("comment 2", slide, secondCommentPosition, createdTime);
for (ICommentAuthor commentAuthor : presentation.getCommentAuthors()) {
List<IComment> commentsToRemove = new ArrayList<IComment>();
IComment[] comments = slide.getSlideComments(commentAuthor);
for (IComment comment : comments) {
if ("comment 1".equals(comment.getText())) {
commentsToRemove.add(comment);
}
}
for (IComment comment : commentsToRemove) {
commentAuthor.getComments().remove(comment);
}
}
presentation.save("pres.pptx", SaveFormat.Pptx);
} finally {
presentation.dispose();
}
FAQ
O Aspose.Slides oferece suporte a um status resolvido para comentários modernos?
Sim. IModernComment.getStatus e IModernComment.setStatus acessam um valor da enumeração ModernCommentStatus, incluindo Resolved. O status é armazenado na apresentação e pode ser lido novamente após o arquivo ser reaberto.
As discussões em tópicos (cadeias de respostas) são suportadas e existe um limite de aninhamento?
Sim. Cada comentário pode referenciar seu parent comment, permitindo cadeias de respostas. A API não define um limite específico de profundidade de aninhamento.
Em qual sistema de coordenadas a posição do marcador de comentário é definida em um slide?
A posição do marcador é definida por coordenadas de ponto flutuante no sistema de coordenadas do slide, permitindo posicioná‑lo precisamente no slide.