2009-08-17 16 views
10

Voglio convertire un file excel in un'immagine (ogni formato è ok) a livello di programmazione (C#). Attualmente sto usando Microsoft Interop Libraries & Office 2007, ma non supporta il salvataggio su un'immagine per impostazione predefinita.Programmare (C#) convertire Excel in un'immagine

Quindi il mio lavoro intorno attuale è la seguente:

  • Aprire file di Excel utilizzando Microsoft Interop;
  • Individuare l'intervallo massimo (che contiene dati);
  • Utilizzare CopyPicture() su tale intervallo, che copierà i dati negli Appunti.

Ora la parte difficile (e miei problemi):

Problema 1:

Uso della classe .NET Appunti, io non sono in grado di ottenere i dati esatti copiati dagli appunti : i dati sono gli stessi, ma in qualche modo la formattazione è distorta (il font dell'intero documento sembra diventare grassetto e un po 'più illeggibile mentre non lo erano); Se si incolla dagli appunti usando mspaint.exe, l'immagine incollata è corretta (e proprio come voglio che sia).

Ho smontato mspaint.exe e ho trovato una funzione che sta utilizzando (OleGetClipboard) per ottenere i dati dagli appunti, ma non riesco a farlo funzionare in C#/.NET.

Altre cose che ho provato erano gli Appunti WINAPI (OpenClipboard, GetClipboardData, CF_ENHMETAFILE), ma i risultati erano gli stessi dell'utilizzo delle versioni .NET.

Problema 2:

Utilizzando la gamma e CopyPicture, se ci sono delle immagini nel foglio Excel, quelle immagini non sono copiati insieme ai dati che circondano negli Appunti.

Parte del codice sorgente di

Excel.Application app = new Excel.Application(); 
app.Visible = app.ScreenUpdating = app.DisplayAlerts = false; 
app.CopyObjectsWithCells = true; 
app.CutCopyMode = Excel.XlCutCopyMode.xlCopy; 
app.DisplayClipboardWindow = false; 

try { 
    Excel.Workbooks workbooks = null; 
    Excel.Workbook book = null; 
    Excel.Sheets sheets = null; 

    try { 
     workbooks = app.Workbooks; 
     book = workbooks.Open(inputFile, false, false, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
           Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
           Type.Missing, Type.Missing); 
     sheets = book.Worksheets; 
    } catch { 
     Cleanup(workbooks, book, sheets); //Cleanup function calls Marshal.ReleaseComObject for all passed objects 
     throw; 
    } 

    for (int i = 0; i < sheets.Count; i++) { 
     Excel.Worksheet sheet = (Excel.Worksheet)sheets.get_Item(i + 1); 

     Excel.Range myrange = sheet.UsedRange; 
     Excel.Range rowRange = myrange.Rows; 
     Excel.Range colRange = myrange.Columns; 

     int rows = rowRange.Count; 
     int cols = colRange.Count; 

     //Following is used to find range with data 
     string startRange = "A1"; 
     string endRange = ExcelColumnFromNumber(cols) + rows.ToString(); 

     //Skip "empty" excel sheets 
     if (startRange == endRange) { 
      Excel.Range firstRange = sheet.get_Range(startRange, endRange); 
      Excel.Range cellRange = firstRange.Cells; 
      object text = cellRange.Text; 
      string strText = text.ToString(); 
      string trimmed = strText.Trim(); 

      if (trimmed == "") { 
       Cleanup(trimmed, strText, text, cellRange, firstRange, myrange, rowRange, colRange, sheet); 
       continue; 
      } 
      Cleanup(trimmed, strText, text, cellRange, firstRange); 
     } 

     Excel.Range range = sheet.get_Range(startRange, endRange); 
     try { 
      range.CopyPicture(Excel.XlPictureAppearance.xlScreen, Excel.XlCopyPictureFormat.xlPicture); 

      //Problem here <------------- 
      //Every attempt to get data from Clipboard fails 
     } finally { 
      Cleanup(range); 
      Cleanup(myrange, rowRange, colRange, sheet); 
     } 
    } //end for loop 

    book.Close(false, Type.Missing, Type.Missing); 
    workbooks.Close(); 

    Cleanup(book, sheets, workbooks); 
} finally { 
    app.Quit(); 
    Cleanup(app); 
    GC.Collect(); 
} 

Ottenere i dati dagli appunti utilizzando WINAPI riesce, ma con cattiva qualità. Fonte:

protected virtual void ClipboardToPNG(string filename) { 
    if (OpenClipboard(IntPtr.Zero)) { 
     if (IsClipboardFormatAvailable((int)CLIPFORMAT.CF_ENHMETAFILE)) { 
      int hEmfClp = GetClipboardDataA((int)CLIPFORMAT.CF_ENHMETAFILE); 

      if (hEmfClp != 0) { 
       int hEmfCopy = CopyEnhMetaFileA(hEmfClp, null); 

       if (hEmfCopy != 0) { 
        Metafile metafile = new Metafile(new IntPtr(hEmfCopy), true); 

        metafile.Save(filename, ImageFormat.Png); 
       } 
      } 
     } 

     CloseClipboard(); 
    } 
} 

Chiunque ha una soluzione? (Sto usando .NET 2.0 btw)

+0

potrebbe condividere il codice sorgente? In che formato vuoi ottenere i dati copiati? Come bitmap? –

risposta

3

lo farà.

Potete vedere il nostro ASP.NET (C# e VB) " Excel Chart e Campioni Range Imaging" campioni here e scaricare una versione di prova gratuita here se si vuole provare.

SpreadsheetGear funziona anche con Windows Form, applicazioni console, ecc. (Non è stato specificato il tipo di applicazione che si sta creando). C'è anche un controllo Windows Form per visualizzare una cartella di lavoro nella tua applicazione se questo è ciò che sei veramente dopo.

Disclaimer: proprio SpreadsheetGear LLC

3

Da quello che ho capito dalla tua domanda non sono in grado di riprodurre il problema.

ho selezionato una gamma manualmente in Excel, ha scelto Copia come immagine con le opzioni come indicato sullo schermo e Bitmap selezionata, poi ho usato il seguente codice per salvare i dati negli appunti:

using System; 
using System.IO; 
using System.Windows; 
using System.Windows.Media.Imaging; 
using System.Drawing.Imaging; 
using Excel = Microsoft.Office.Interop.Excel; 

public class Program 
{ 
    [STAThread] 
    static void Main(string[] args) 
    { 
     Excel.Application excel = new Excel.Application(); 
     Excel.Workbook wkb = excel.Workbooks.Add(Type.Missing); 
     Excel.Worksheet sheet = wkb.Worksheets[1] as Excel.Worksheet; 
     Excel.Range range = sheet.Cells[1, 1] as Excel.Range; 
     range.Formula = "Hello World"; 

     // copy as seen when printed 
     range.CopyPicture(Excel.XlPictureAppearance.xlPrinter, Excel.XlCopyPictureFormat.xlPicture); 

     // uncomment to copy as seen on screen 
     //range.CopyPicture(Excel.XlPictureAppearance.xlScreen, Excel.XlCopyPictureFormat.xlBitmap); 

     Console.WriteLine("Please enter a full file name to save the image from the Clipboard:"); 
     string fileName = Console.ReadLine(); 
     using (FileStream fileStream = new FileStream(fileName, FileMode.Create)) 
     { 
      if (Clipboard.ContainsData(System.Windows.DataFormats.EnhancedMetafile)) 
      { 
       Metafile metafile = Clipboard.GetData(System.Windows.DataFormats.EnhancedMetafile) as Metafile; 
       metafile.Save(fileName); 
      } 
      else if (Clipboard.ContainsData(System.Windows.DataFormats.Bitmap)) 
      { 
       BitmapSource bitmapSource = Clipboard.GetData(System.Windows.DataFormats.Bitmap) as BitmapSource; 

       JpegBitmapEncoder encoder = new JpegBitmapEncoder(); 
       encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); 
       encoder.QualityLevel = 100; 
       encoder.Save(fileStream); 
      } 
     } 
     object objFalse = false; 
     wkb.Close(objFalse, Type.Missing, Type.Missing); 
     excel.Quit(); 
    } 
} 

Per quanto riguarda il tuo secondo problema: per quanto ne so non è possibile in Excel selezionare contemporaneamente un intervallo di celle e un'immagine. Se si desidera ottenere entrambi in un'immagine allo stesso tempo, potrebbe essere necessario stampare il foglio di Excel in un file immagine/PDF/XPS.

+0

Grazie per l'aggiornamento. Nel mio caso, Clipboard.ContainsData (DataFormats.EnhancedMetafile) restituisce true, ma in seguito l'Clipboard.GetData (DataFormats.EnhancedMetafile) restituisce sempre null. mspaint.exe (come sempre) è in grado di incollare i dati che vengono copiati negli appunti da CopyPicture() – Zurb

+0

@Zurb: si ottiene 'null' da' Clipboard.GetData' anche quando si esegue il codice di esempio sopra ? Si noti che sta utilizzando la classe degli Appunti WPF in 'System.Windows' e non quella dei moduli di Windows. Puoi verificare che nessun'altra applicazione blocchi gli appunti? –

+0

@divo: Ho dimenticato di menzionare che sto usando .NET 2.0, quindi non posso eseguire il tuo codice completamente come pubblicato. Ho aggiunto parte del mio codice sorgente alla domanda. – Zurb

1

Perché filo asp.net non ha il diritto di accedere ApartmentState Appunti di classe, quindi è necessario scrivere codice per accedere Appunti nel nuovo thread. Per esempio:

private void AccessClipboardThread() 
{ 
    // access clipboard here normaly 
} 

nel thread principale:

.... 
Excel.Range range = sheet.get_Range(startRange, endRange); //Save range image to clipboard 
Thread thread = new Thread(new ThreadStart(AccessClipboardThread)); 
thread.ApartmentState = ApartmentState.STA; 
thread.Start(); 
thread.Join(); //main thread will wait until AccessClipboardThread finish. 
.... 
+0

Il codice che ho postato è stato implementato per funzionare in un'applicazione C# Windows standard (ancora non funzionante btw), sebbene stavo progettando di usarlo sul web. Grazie comunque. – Zurb

+0

Posso confermare che l'utilizzo di questo funziona per una soluzione web. – Dave

0

È interessante notare che ho fatto questo in un vano STA per qualche tempo con successo. Ho scritto un'app che viene eseguita su base settimanale e invia report di stato del progetto, inclusi alcuni grafici che generano a livello di programmazione utilizzando Excel.

Ieri sera non è riuscito a restituire null tutti i grafici. Sto eseguendo il debug oggi e non trovo alcuna spiegazione, solo che il metodo Clipboard.GetImage() restituisce null all'improvviso, cosa che non è avvenuta. Impostando un punto di interruzione a questa chiamata, posso effettivamente dimostrare (premendo CTRL + V in MS-Word) che l'immagine è effettivamente negli appunti. Ahimè continua su Clipboard.GetImage() restituisce null (se sto ficcanaso come questo o no).

Il mio codice viene eseguito come app per console e il metodo Main ha l'attributo [STAThread]. Lo faccio il debug come un'app di Windows Form (tutto il mio codice è in una libreria e ho semplicemente due front-end per quello).

Entrambi restituiscono null oggi.

Fuori interesse Spengo il feticcio del grafico in un thread come delineato (e si noti che thread.ApartmentState è deprecato), e viene eseguito dolce, ma vengono comunque restituiti i valori nulli.

Bene, ho rinunciato e ho fatto ciò che qualsiasi geek IT avrebbe fatto, riavviato.

Voila ... tutto andava bene. Vai a capire ... è per questo che detestiamo tutti i computer, Microsoft Windows e Microsoft Office? Hmmmm ... C'è qualcosa, qualcosa di completamente transitorio che può accadere al tuo PC che rende Clipboard.GetImage() fallito!

1

Questo è un bug con GDI + quando si tratta di convertire i metafile in un formato di mappa di bit.
Succede per molti EMF che visualizzano grafici con testi. Per ricreare, è sufficiente creare un grafico in Excel che visualizzi i dati per il proprio asse X e Y. Copia il grafico come immagine e incolla la parola come metafile. Apri il documento e vedrai un EMF nella cartella media. Se ora apri quell'EMF in qualsiasi programma di pittura basato su Windows che lo converte in una bitmap, vedrai distorsioni, in particolare, il testo e le linee diventano più grandi e distorti. Sfortunatamente, è uno di quei problemi che Microsoft non è in grado di ammettere o fare nulla. Speriamo che Google e Apple assumano presto anche il mondo dell'ufficio/word processing.

0

Se non ti dispiace Linux (stile), puoi usare OpenOffice (o LibreOffice) per convertire prima xls in pdf, quindi utilizzare ImageMagic per convertire il pdf in immagine. Una guida di base può essere trovata a http://www.novell.com/communities/node/5744/c-linux-thumbnail-generation-pdfdocpptxlsimages.

Inoltre, sembrano esserci API .Net per entrambi i programmi menzionati sopra. Vedi: http://www.opendocument4all.com/download/OpenOffice.net.pdf e http://imagemagick.net/script/api.php#dot-net