2017-12-05 4 views
0

Mit Apache-POI versuche ich ein Bild in eine Excel-Tabelle mit einer oberen linken Ecke in der Mitte einer Zeile mit einer großen Schrift (Calibri-32) einzufügen.
Mit den bekannten Formeln habe ich herausgefunden, dass der dy1 Wert innerhalb der XSSFClientAnchor etwa 260.000 sein sollte.
Beim Öffnen der Excel-Datei bekomme ich jedoch eine Fehlermeldung, dass der Inhalt nicht verwertbar ist.
Die Warnung wird angenommen, das Bild wird trotzdem korrekt angezeigt.Maximaler dy-Wert für XSSFClientAnchor

Nach einigen Tests habe ich herausgefunden, dass der maximale Wert der dy, ohne einen Fehler von Excel zu erhalten, scheint 190,500.
Die Schriftart, die ich verwende, ergibt eine Zeilenhöhe von 55 Pixel.
In der Mitte der Reihe ist daher 0.5*55*Units.EMU_PER_PIXEL=261,938.

Das gleiche Problem tritt auf, wenn Sie eine kleinere Schriftart verwenden, das Bild aber am Ende der Zeile beginnen soll.
In allen Fällen erhalte ich einen Fehler, wenn der dy1 einen Wert größer als 190500 hat.

Gibt es jemanden, der eine Ahnung hat?

UPDATE:
extrahiert ich die xml aus der xlsx-Datei, und ich bemerkte einen negativen cy irgendwo Wert. Ich bin nicht wirklich vertraut mit dem xlsx Inhalt, aber ich hoffe, dass es für jemanden nützlich ist:

<xdr:wsDr xmlns:xdr="http://schemas.openxmlformats.org/drawingml/2006/spreadsheetDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"> 
    <xdr:twoCellAnchor editAs="oneCell"> 
    <xdr:from> 
     <xdr:col>2</xdr:col> 
     <xdr:colOff>147637</xdr:colOff> 
     <xdr:row>0</xdr:row> 
     <xdr:rowOff>261937</xdr:rowOff> 
    </xdr:from> 
    <xdr:to> 
     <xdr:col>5</xdr:col> 
     <xdr:colOff>2309812</xdr:colOff> 
     <xdr:row>13</xdr:row> 
     <xdr:rowOff>14287</xdr:rowOff> 
    </xdr:to> 
    <xdr:pic> 
     <xdr:nvPicPr> 
     <xdr:cNvPr id="1" name="Picture 1" descr="Picture"/> 
     <xdr:cNvPicPr> 
      <a:picLocks noChangeAspect="true"/> 
     </xdr:cNvPicPr> 
     </xdr:nvPicPr> 
     <xdr:blipFill> 
     <a:blip r:embed="rId1"/> 
     <a:stretch> 
      <a:fillRect/> 
     </a:stretch> 
     </xdr:blipFill> 
     <xdr:spPr> 
     <a:xfrm> 
      <a:off x="147637" y="261937"/> 
      <a:ext cx="195263" cy="-71437"/> 
     </a:xfrm> 
     <a:prstGeom prst="rect"> 
      <a:avLst/> 
     </a:prstGeom> 
     </xdr:spPr> 
    </xdr:pic> 
    <xdr:clientData/> 
    </xdr:twoCellAnchor> 
</xdr:wsDr> 

UPDATE 2:
Der folgende Code den Fehler zeigt. Es tritt auf, wenn dy1 größer als 190.500 ist und row2 gleich row1 + 1

/********************************************************************************************************************** 
* Package specification 
*********************************************************************************************************************/ 
package test; 

/********************************************************************************************************************** 
* Import definitions 
*********************************************************************************************************************/ 
import java.awt.Desktop; 
import java.io.*; 
import org.apache.poi.ss.usermodel.*; 
import org.apache.poi.util.*; 
import org.apache.poi.xssf.streaming.*; 
import org.apache.poi.xssf.usermodel.*; 

/********************************************************************************************************************** 
* This class implements a Minimal, Complete and Verifiable example for the problem of the maximum dy value for the 
* {@link XSSFClientAnchor}. 
*********************************************************************************************************************/ 
public class TestPictureOffset 
{ 
    /******************************************************************************************************************** 
    * This constants represents the name of the file with the picture to import within the sheet. 
    *******************************************************************************************************************/ 
    private static final String FILENAME_PICTURE = "./excel.png"; 

    /******************************************************************************************************************** 
    * These constants represents the width and height of the big cell within the sheet. 
    *******************************************************************************************************************/ 
    private static final short BIG_CELL_COLUMN_WIDTH_IN_PIXELS = 317; 
    private static final short BIG_CELL_ROW_HEIGHT_IN_PIXELS = 56; 

    /******************************************************************************************************************** 
    * This constants represents the default height of a cell within the sheet. 
    *******************************************************************************************************************/ 
    private static final short DEFAULT_ROW_HEIGHT_IN_PIXELS = 20; 

    /******************************************************************************************************************** 
    * This method places the specified picture on the sheet. 
    *******************************************************************************************************************/ 
    private static void setPicture(int  picture_index, 
           SXSSFSheet sheet) 
    { 
    // ----------------- 
    // Initialize anchor 
    // ----------------- 
    XSSFClientAnchor anchor; 
    anchor = (XSSFClientAnchor)sheet.getWorkbook().getCreationHelper().createClientAnchor(); 
    anchor.setAnchorType(XSSFClientAnchor.AnchorType.MOVE_AND_RESIZE); 

    // ----------------------------- 
    // Set position 
    // THIS IS WHERE THE FUN HAPPENS 
    // ----------------------------- 
    anchor.setCol1(1); 
    anchor.setRow1(0); 
    anchor.setDx1((int)(0.5 * BIG_CELL_COLUMN_WIDTH_IN_PIXELS * Units.EMU_PER_PIXEL)); 
    anchor.setDy1((int)(0.4 * BIG_CELL_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); 
    anchor.setCol2(anchor.getCol1() + 1); 
    anchor.setRow2(anchor.getRow1() + 1); // Fails if dy1 > 190500 
    //anchor.setRow2(anchor.getRow1() + 2); // OK independently from dy1 
    anchor.setDx2(0); 
    anchor.setDy2(0); 

    // ---------------------- 
    // Show some measurements 
    // ---------------------- 
    System.out.println("Got dy1: " + anchor.getDy1()); 
    System.out.println("Maximum dy in default cell: " + (DEFAULT_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); 

    // ---------------- 
    // Draw the picture 
    // ---------------- 
    sheet.createDrawingPatriarch().createPicture(anchor, picture_index); 

    } // setPicture 

    /******************************************************************************************************************** 
    * This method runs the application. 
    *******************************************************************************************************************/ 
    private static void run() 
    throws Exception 
    { 
    // --------------- 
    // Create workbook 
    // --------------- 
    SXSSFWorkbook workbook; 
    workbook = new SXSSFWorkbook(); 
    workbook.setCompressTempFiles(true); 

    // ------------ 
    // Create sheet 
    // ------------ 
    SXSSFSheet sheet; 
    sheet = workbook.createSheet("TestSheet"); 
    sheet.trackAllColumnsForAutoSizing(); 

    // -------------------------- 
    // Create style with big font 
    // -------------------------- 
    Font   font; 
    XSSFCellStyle style; 
    font = workbook.createFont(); 
    font.setFontHeightInPoints((short)32); 
    style = (XSSFCellStyle)workbook.createCellStyle(); 
    style.setFont(font); 

    // ------------------- 
    // Write something big 
    // ------------------- 
    SXSSFRow row; 
    SXSSFCell cell; 
    row = sheet.createRow(0); 
    cell = row.createCell(1); 
    cell.setCellStyle(style); 
    cell.setCellValue("Hello everybody"); 

    // ----------------------- 
    // Auto resize this column 
    // ----------------------- 
    sheet.autoSizeColumn(1); 

    // ------------ 
    // Load picture 
    // ------------ 
    InputStream input_stream; 
    byte[]  bytes; 
    input_stream = new FileInputStream(FILENAME_PICTURE); 
    bytes = IOUtils.toByteArray(input_stream); 
    input_stream.close(); 

    // --------------- 
    // Add to workbook 
    // --------------- 
    int picture_index; 
    picture_index = workbook.addPicture(bytes, SXSSFWorkbook.PICTURE_TYPE_PNG); 

    // ------------------------- 
    // Position picture in sheet 
    // ------------------------- 
    setPicture(picture_index, sheet); 

    // ------------- 
    // Save workbook 
    // ------------- 
    File    output_file; 
    FileOutputStream output_stream; 
    output_file = new File("testxls.xlsx"); 
    output_stream = new FileOutputStream(output_file); 
    workbook.write(output_stream); 
    output_stream.close(); 
    workbook.close(); 

    // ------- 
    // Open it 
    // ------- 
    Desktop.getDesktop().open(output_file); 

    } // run 

    /******************************************************************************************************************** 
    *            M A I N 
    *******************************************************************************************************************/ 
    public static void main(String[] args) 
    { 
    try 
    { 
     run(); 
    } 
    catch (Exception exception) 
    { 
     exception.printStackTrace(); 
    } 

    } // main 

} // class TestPictureOffset 
+1

Statt viel prosa Sie den Code zeigen sollte, die diese 'xl \ Zeichnungen \ drawing1.xml' erzeugt. Siehe [Erstellen eines minimalen, vollständigen und überprüfbaren Beispiels] (https://stackoverflow.com/help/mcve). –

+0

Der negative 'cy' hat nichts mit dem Anker zu tun, der im ersten Teil des' XML' steht und für mich gut aussieht. Die Frage ist also, woher das negative 'cy' kommt. Also wird der Code benötigt. –

+0

Es wird eine Weile dauern, bis ich dieses minimale, vollständige und nachprüfbare Beispiel erstellt habe, aber ich werde so schnell wie möglich zurück sein. –

Antwort

1

Also, wo wir brauchen die Bestimmung der negative cy Mai herkommt.

Zuerst: Sie sollten keine Konstanten für BIG_CELL_COLUMN_WIDTH_IN_PIXELS und BIG_CELL_ROW_HEIGHT_IN_PIXELS verwenden. Stattdessen tun Sie das gleiche wie Apache poi und verwenden Sie sheet.getColumnWidthInPixels und ImageUtils.getRowHeightInPixels, um Breite und Höhe zu berechnen.

Und Sie müssen Zeilenhöhe entsprechend der Schrifthöhe einstellen. Andernfalls bleibt die Zeile in der Standardhöhe, bis das Blatt in Sicht darstellt. Und in der Standardzeilenhöhe liegt die dy1 außerhalb der Zeile, wenn sie größer als 190500 ist. Es wäre also kein gültiger dy1 für diese Zeile.

Es gibt private CTTransform2D createXfrm(XSSFClientAnchor anchor) in XSSFDrawing.java, die die xfrm mit dem cy berechnet. Dort können Sie sehen, dass ein negativer cy nur auftritt, wenn height + anchor.getDy2() niedriger ist als anchor.getDy1().

Mit XSSF wäre das genug. Aber mit SXSSF scheint die Berechnung der Zeilenhöhe in private CTTransform2D createXfrm(XSSFClientAnchor anchor) mit ImageUtils.getRowHeightInPixels(sheet, row) falsch zu sein. Ich vermute, das liegt daran, dass die Zeilenhöhe aufgrund der Streaming-Art nicht bereits im Blatt bekannt ist. Also brauchen wir einen Workaround. Legen Sie die Standardzeilenhöhe so fest wie für die große Schriftart erforderlich fest, legen Sie das Bild dann fest und setzen Sie anschließend die Standardzeilenhöhe zurück.

Die folgenden Werke für mich:

/********************************************************************************************************************** 
* Import definitions 
*********************************************************************************************************************/ 
import java.awt.Desktop; 
import java.io.*; 
import org.apache.poi.ss.usermodel.*; 
import org.apache.poi.util.*; 
import org.apache.poi.xssf.streaming.*; 
import org.apache.poi.xssf.usermodel.*; 

import org.apache.poi.ss.util.ImageUtils; 

/********************************************************************************************************************** 
* This class implements a Minimal, Complete and Verifiable example for the problem of the maximum dy value for the 
* {@link XSSFClientAnchor}. 
*********************************************************************************************************************/ 
public class TestPictureOffset 
{ 
    /******************************************************************************************************************** 
    * This constants represents the name of the file with the picture to import within the sheet. 
    *******************************************************************************************************************/ 
    private static final String FILENAME_PICTURE = "./excel.png"; 

    /******************************************************************************************************************** 
    * These constants represents the width and height of the big cell within the sheet. 
    *******************************************************************************************************************/ 
    // Don't do that. Instead do the same as apache poi does and use 
    // sheet.getColumnWidthInPixels and 
    // ImageUtils.getRowHeightInPixels to calculate width and height 

    private static final short BIG_CELL_COLUMN_WIDTH_IN_PIXELS = 317; 
    private static final short BIG_CELL_ROW_HEIGHT_IN_PIXELS = 56; 

    /******************************************************************************************************************** 
    * This constants represents the default height of a cell within the sheet. 
    *******************************************************************************************************************/ 
    private static final short DEFAULT_ROW_HEIGHT_IN_PIXELS = 20; 

    /******************************************************************************************************************** 
    * This method places the specified picture on the sheet. 
    *******************************************************************************************************************/ 
    private static void setPicture(int  picture_index, 
           Sheet sheet) 
    { 
    // ----------------- 
    // Initialize anchor 
    // ----------------- 
    ClientAnchor anchor; 
    anchor = sheet.getWorkbook().getCreationHelper().createClientAnchor(); 
    anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE); 

    // ----------------------------- 
    // Set position 
    // THIS IS WHERE THE FUN HAPPENS 
    // ----------------------------- 
    anchor.setCol1(1); 
    anchor.setRow1(0); 
    //anchor.setDx1((int)(0.5 * BIG_CELL_COLUMN_WIDTH_IN_PIXELS * Units.EMU_PER_PIXEL)); 
    //anchor.setDy1((int)(0.4 * BIG_CELL_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); 
    anchor.setDx1((int)(0.5 * sheet.getColumnWidthInPixels(1) * Units.EMU_PER_PIXEL)); 
    anchor.setDy1((int)(0.4 * ImageUtils.getRowHeightInPixels(sheet, 0) * Units.EMU_PER_PIXEL)); 
    anchor.setCol2(anchor.getCol1() + 1); 
    anchor.setRow2(anchor.getRow1() + 1); // Fails if dy1 > 190500 
    //anchor.setRow2(anchor.getRow1() + 2); // OK independently from dy1 
    anchor.setDx2(0); 
    anchor.setDy2(0); 

    // ---------------------- 
    // Show some measurements 
    // ---------------------- 
    System.out.println("Got dy1: " + anchor.getDy1()); 
    System.out.println("Maximum dy in default cell: " + (DEFAULT_ROW_HEIGHT_IN_PIXELS * Units.EMU_PER_PIXEL)); 

    // ---------------- 
    // Draw the picture 
    // ---------------- 
    sheet.createDrawingPatriarch().createPicture(anchor, picture_index); 

    } // setPicture 

    /******************************************************************************************************************** 
    * This method runs the application. 
    *******************************************************************************************************************/ 
    private static void run() 
    throws Exception 
    { 
    // --------------- 
    // Create workbook 
    // --------------- 
    SXSSFWorkbook workbook; 
    workbook = new SXSSFWorkbook(); 
    workbook.setCompressTempFiles(true); 

    // ------------ 
    // Create sheet 
    // ------------ 
    SXSSFSheet sheet; 
    sheet = workbook.createSheet("TestSheet"); 
    sheet.trackAllColumnsForAutoSizing(); 

    // -------------------------- 
    // Create style with big font 
    // -------------------------- 
    Font   font; 
    CellStyle style; 
    font = workbook.createFont(); 
    font.setFontHeightInPoints((short)32); 
    style = workbook.createCellStyle(); 
    style.setFont(font); 

    // ------------------- 
    // Write something big 
    // ------------------- 
    SXSSFRow row; 
    SXSSFCell cell; 
    row = sheet.createRow(0); 
    cell = row.createCell(1); 
    cell.setCellStyle(style); 
    cell.setCellValue("Hello everybody"); 

    // ----------------------- 
    // Set row heigth according to the fonts height 
    // ----------------------- 
    row.setHeightInPoints(workbook.getFontAt(style.getFontIndex()).getFontHeightInPoints()*1.275f); 

    // ----------------------- 
    // Auto resize this column 
    // ----------------------- 
    sheet.autoSizeColumn(1); 

    // ------------ 
    // Load picture 
    // ------------ 
    InputStream input_stream; 
    byte[]  bytes; 
    input_stream = new FileInputStream(FILENAME_PICTURE); 
    bytes = IOUtils.toByteArray(input_stream); 
    input_stream.close(); 

    // --------------- 
    // Add to workbook 
    // --------------- 
    int picture_index; 
    picture_index = workbook.addPicture(bytes, SXSSFWorkbook.PICTURE_TYPE_PNG); 

    // ------------------------- 
    // Position picture in sheet 
    // ------------------------- 
    // Workaround for SXSSFSheet which has not the correct row height in 
    // private CTTransform2D createXfrm(XSSFClientAnchor anchor) 
    // because of the streaming manner. 
    // So set default row height that big: 
    sheet.setDefaultRowHeight(sheet.getRow(0).getHeight()); 
    // set the picture then: 
    setPicture(picture_index, sheet); 
    // and reset the default row height after that: 
    sheet.setDefaultRowHeight((short)(15*20)); 

    // ------------- 
    // Save workbook 
    // ------------- 
    File    output_file; 
    FileOutputStream output_stream; 
    output_file = new File("testxls.xlsx"); 
    output_stream = new FileOutputStream(output_file); 
    workbook.write(output_stream); 
    output_stream.close(); 
    workbook.close(); 

    workbook.dispose(); 

    // ------- 
    // Open it 
    // ------- 
    Desktop.getDesktop().open(output_file); 

    } // run 

    /******************************************************************************************************************** 
    *            M A I N 
    *******************************************************************************************************************/ 
    public static void main(String[] args) 
    { 
    try 
    { 
     run(); 
    } 
    catch (Exception exception) 
    { 
     exception.printStackTrace(); 
    } 

    } // main 

} // class TestPictureOffset 
+0

Ich habe die Konstanten nur innerhalb des Beispiels verwendet. Der tatsächliche Quellcode berechnet die Breite und Höhe, wobei berücksichtigt wird, dass eine einzelne Zeile unterschiedliche Schriftarten enthalten kann und dass die Standardschrift von Calibri-11 abweichen kann. Wie auch immer, ich setze jetzt die Standardzeilenhöhe vorübergehend auf die Höhe der ersten Zeile, die (teilweise) vom Bild abgedeckt wird, füge dann das Bild hinzu und stelle schließlich die Standardzeilenhöhe wieder her. Funktioniert super. Vielen Dank. –