如何生成一个竖向的文本框,并且插入的富文本内容需要旋转 -90 度

我使用words.java 试图在word中生成类似sidebar的功能,插入的竖向形状后插入富文本内容,我想知道怎么才能把富文本中的文字和图片都旋转 -90 ° 并保持内容正确,以下是我的代码

 private void insertSidebarIntoHeader(Document document, HeaderFooter header, String htmlContent) throws Exception {
        // Get the section that contains the header to determine page dimensions
        Section section = (Section) header.getParentNode();
        PageSetup pageSetup = section.getPageSetup();
        
        // Calculate content area height (excludes top and bottom margins)
        double pageHeight = pageSetup.getPageHeight();
        double topMargin = pageSetup.getTopMargin();
        double bottomMargin = pageSetup.getBottomMargin();
        double availableHeight = pageHeight - topMargin - bottomMargin;
        
        // Use 100% of available height to match body content area exactly
        double sidebarHeight = availableHeight;
        
        log.debug("Left sidebar dimensions: pageHeight={}pt, topMargin={}pt, bottomMargin={}pt, availableHeight={}pt, sidebarHeight={}pt (100%)",
                pageHeight, topMargin, bottomMargin, availableHeight, sidebarHeight);
        
        // ★ CRITICAL: Create a DrawingML Shape using RECTANGLE (not TEXT_BOX)
        // This ensures Word recognizes it as a rotatable shape with full UI support
        Shape sidebar = new Shape(document, ShapeType.RECTANGLE);
        
        // ★ CRITICAL: Set dimensions for BEFORE rotation
        // After -90 degree rotation, width becomes height and height becomes width
        // So to get a narrow (40pt) × tall (sidebarHeight) sidebar after rotation:
        // - Set width = sidebarHeight (will become height after rotation)
        // - Set height = 40 (will become width after rotation)
        sidebar.setWidth(sidebarHeight);  // Will become height after rotation
        sidebar.setHeight(40);            // Will become width (narrow) after rotation
        
        log.debug("Sidebar dimensions before rotation: width={}pt (will be height), height=40pt (will be width)", 
                sidebarHeight);
        
        // CRITICAL: Set to floating (not inline) for fixed positioning
        sidebar.setWrapType(WrapType.NONE);
        
        // Mark as decorative shape (not part of content flow)
        sidebar.setBehindText(false); // Show in front of text
        sidebar.setAllowOverlap(true); // Allow overlap with other content
        
        // Position relative to PAGE for absolute positioning
        sidebar.setRelativeHorizontalPosition(RelativeHorizontalPosition.PAGE);
        sidebar.setRelativeVerticalPosition(RelativeVerticalPosition.PAGE);
        
        // ★ CRITICAL: Position calculation based on CENTER POINT rotation
        // Rotation happens around the shape's center, not the anchor point
        //
        // Target position AFTER rotation:
        // - Rotated shape: width=40pt, height=sidebarHeight
        // - Left edge at 15pt from page → Center X = 15 + 40/2 = 35pt
        // - Top edge at topMargin → Center Y = topMargin + sidebarHeight/2
        //
        // BEFORE rotation (horizontal shape: width=sidebarHeight, height=40):
        // - Center X = 35pt (same as after rotation)
        // - Center Y = topMargin + sidebarHeight/2 (same as after rotation)
        // - Therefore: left = centerX - width/2 = 35 - sidebarHeight/2
        // - Therefore: top = centerY - height/2 = (topMargin + sidebarHeight/2) - 20
        
        double targetCenterX = 35;  // 15 (left edge) + 20 (half of rotated width 40pt)
        double targetCenterY = topMargin + sidebarHeight / 2;
        
        double leftBeforeRotation = targetCenterX - sidebarHeight / 2;
        double topBeforeRotation = targetCenterY - 20;  // 20 = 40pt / 2
        
        sidebar.setLeft(leftBeforeRotation);
        sidebar.setTop(topBeforeRotation);
        
        log.debug("Sidebar position before rotation: left={}pt, top={}pt (center-based calculation)", 
                leftBeforeRotation, topBeforeRotation);
        log.debug("Target center point: X={}pt, Y={}pt", targetCenterX, targetCenterY);
        
        // Remove border and background (clean appearance)
        sidebar.setStroked(false); // No border
        sidebar.getFill().setVisible(false); // No background fill
        
        // ★ CRITICAL: Configure TextBox to ensure content rotates with the Shape
        // Content in TextBox follows the Shape's geometric rotation
        TextBox textBox = sidebar.getTextBox();
        
        // Configure TextBox layout properties
        textBox.setVerticalAnchor(VerticalAlignment.CENTER);
        textBox.setInternalMarginTop(10);
        textBox.setInternalMarginBottom(10);
        textBox.setInternalMarginLeft(10);
        textBox.setInternalMarginRight(10);
        textBox.setFitShapeToText(false);
        
        // Create paragraph for TextBox content
        Paragraph textPara = new Paragraph(document);
        textPara.getParagraphFormat().setAlignment(ParagraphAlignment.LEFT);
        textPara.getParagraphFormat().setLineSpacing(12);
        
        // ★ CRITICAL: Add paragraph to Shape (which places it in the TextBox)
        // This ensures the paragraph rotates with the Shape
        sidebar.appendChild(textPara);
        
        // Insert HTML content using DocumentBuilder
        DocumentBuilder builder = new DocumentBuilder(document);
        builder.moveTo(textPara);
        
        // Ensure horizontal text direction (left-to-right, not bidirectional)
        builder.getParagraphFormat().setBidi(false);
        
        builder.insertHtml(htmlContent);
        
        // ★ CRITICAL: Apply true geometric rotation to the entire Shape
        // This rotates the entire DrawingML Shape (text + images + coordinate system)
        // -90 degrees = 90 degrees counter-clockwise = vertical sidebar on the left
        sidebar.setRotation(-90.0); // Rotate 90 degrees counter-clockwise
        
        // CRITICAL: Create a new paragraph as container for the sidebar shape
        // This is the proper way to add shapes to headers according to Aspose.Words documentation
        Paragraph sidebarPara = new Paragraph(document);
        sidebarPara.appendChild(sidebar);
        
        // Ensure header exists
        if (header.getChildNodes(com.aspose.words.NodeType.PARAGRAPH, false).getCount() == 0) {
            header.appendChild(new Paragraph(document));
        }
        
        // Add the sidebar paragraph to the header
        // Insert at the beginning of the header (before any existing content)
        header.insertBefore(sidebarPara, header.getFirstChild());
        
        log.debug("Sidebar DrawingML Shape (RECTANGLE) inserted and rotated -90 degrees (entire shape including all content)");
    }

@kaihufreyrsolutions.com

您正在尝试在 Aspose.Words for Java 中创建一个竖向的文本框,并让其中的富文本内容(包括文字和图片)旋转 -90 度。您的代码已经正确地将内容插入到 Shape 的 TextBox 中,并对整个 Shape 应用了旋转 。

回答:

  1. 您当前的做法是正确的:您将内容添加到了 Shape 的 TextBox 中,这样内容会随着 Shape 的几何变换(例如旋转)而旋转 。
  2. 旋转是通过设置 Shape 的 setRotation(-90.0) 实现的,这会将整个 Shape(包括其内容)旋转 -90 度 。
  3. 富文本内容(HTML)会随 Shape 一起旋转,只要内容是作为 Shape 的子节点插入的,即您已将其添加到 TextBox 中 。

因此,您无需额外操作即可实现文字和图片的旋转。如果旋转后内容显示不正确,请检查是否所有内容都已正确添加到 Shape 的 TextBox 中,并确认没有其他布局或格式设置干扰旋转效果。

如果您发现内容未按预期旋转,可以进一步确认:

  • 所有内容是否确实被添加到 sidebar.appendChild(textPara) 之后的 TextBox 中;
  • 是否有其他样式或格式影响了内容的显示。

如有更多细节,可提供截图或示例文档以便进一步分析。

a31e3aa2-5b8e-4579-ba31-16db9a8d1646_debug.docx (45.2 KB)

但是实际文档中,这个文本和图片没有旋转,还是水平的

@kaihufreyrsolutions.com 在你的情况下,使用 LayoutFlow 比文本旋转更好。请看下面修改后的代码:

private static void insertSidebarIntoHeader(Document document, HeaderFooter header, String htmlContent) throws Exception {
    // Get the section that contains the header to determine page dimensions
    Section section = (Section) header.getParentNode();
    PageSetup pageSetup = section.getPageSetup();
        
    // Calculate content area height (excludes top and bottom margins)
    double pageHeight = pageSetup.getPageHeight();
    double topMargin = pageSetup.getTopMargin();
    double bottomMargin = pageSetup.getBottomMargin();
    double availableHeight = pageHeight - topMargin - bottomMargin;
        
    // Use 100% of available height to match body content area exactly
    double sidebarHeight = availableHeight;
        
    // Ensure header exists
    if (header.getChildNodes(com.aspose.words.NodeType.PARAGRAPH, false).getCount() == 0) {
        header.appendChild(new Paragraph(document));
    }
    // ★ CRITICAL: Create a DrawingML Shape using RECTANGLE (not TEXT_BOX)
    // This ensures Word recognizes it as a rotatable shape with full UI support
    DocumentBuilder builder = new DocumentBuilder(document);
    builder.moveTo(header.getFirstParagraph());
    Shape sidebar = builder.insertShape(ShapeType.RECTANGLE, 40, sidebarHeight);
        
    // CRITICAL: Set to floating (not inline) for fixed positioning
    sidebar.setWrapType(WrapType.NONE);
        
    // Mark as decorative shape (not part of content flow)
    sidebar.setBehindText(false); // Show in front of text
    sidebar.setAllowOverlap(true); // Allow overlap with other content
        
    // Position relative to PAGE for absolute positioning
    sidebar.setRelativeHorizontalPosition(RelativeHorizontalPosition.PAGE);
    sidebar.setRelativeVerticalPosition(RelativeVerticalPosition.PAGE);
        
    sidebar.setLeft(0);
    sidebar.setTop(topMargin);
        
    // Remove border and background (clean appearance)
    sidebar.setStroked(false); // No border
    sidebar.getFill().setVisible(false); // No background fill
        
    // ★ CRITICAL: Configure TextBox to ensure content rotates with the Shape
    // Content in TextBox follows the Shape's geometric rotation
    TextBox textBox = sidebar.getTextBox();
        
    // Configure TextBox layout properties
    textBox.setVerticalAnchor(VerticalAlignment.CENTER);
    textBox.setInternalMarginTop(10);
    textBox.setInternalMarginBottom(10);
    textBox.setInternalMarginLeft(10);
    textBox.setInternalMarginRight(10);
    textBox.setFitShapeToText(false);
    textBox.setLayoutFlow(LayoutFlow.BOTTOM_TO_TOP);
        
    // Create paragraph for TextBox content
    Paragraph textPara = new Paragraph(document);
    textPara.getParagraphFormat().setAlignment(ParagraphAlignment.LEFT);
    textPara.getParagraphFormat().setLineSpacing(12);
        
    // ★ CRITICAL: Add paragraph to Shape (which places it in the TextBox)
    // This ensures the paragraph rotates with the Shape
    sidebar.appendChild(textPara);
        
    // Insert HTML content using DocumentBuilder
    //DocumentBuilder builder = new DocumentBuilder(document);
    builder.moveTo(textPara);
    
    builder.insertHtml(htmlContent);
}

out.docx (9.3 KB)

谢谢,我发现是 Shape sidebar = builder.insertShape(ShapeType.RECTANGLE, 40, sidebarHeight);
和 Shape sidebar = new Shape(document, ShapeType.RECTANGLE); 这两种构造Shape方式不同产生的问题,现在已经解决问题了。