2009-08-17 38 views
10

Tôi muốn chuyển đổi tệp excel thành hình ảnh (mọi định dạng đều ok) theo lập trình (C#). Hiện tại tôi đang sử dụng Thư viện Microsoft Interop & Office 2007, nhưng nó không hỗ trợ lưu vào hình ảnh theo mặc định.Lập trình (C#) chuyển đổi Excel sang hình ảnh

Vì vậy, công việc xung quanh hiện tại của tôi là như sau:

  • mở file Excel sử dụng Microsoft Interop;
  • Tìm hiểu phạm vi tối đa (có chứa dữ liệu);
  • Sử dụng CopyPicture() trên phạm vi đó, sẽ sao chép dữ liệu vào Clipboard.

Bây giờ là phần khó khăn (và các vấn đề của tôi):

Vấn đề 1:

Sử dụng lớp .NET Clipboard, tôi không thể nhận được các dữ liệu chính xác sao chép từ clipboard : dữ liệu giống nhau, nhưng bằng cách nào đó định dạng bị bóp méo (phông chữ của toàn bộ tài liệu dường như trở nên đậm và khó đọc hơn một chút trong khi chúng không được); Nếu tôi dán từ clipboard bằng mspaint.exe, hình ảnh được dán là chính xác (và giống như tôi muốn nó).

Tôi đã tháo rời mspaint.exe và tìm thấy một chức năng mà nó đang sử dụng (OleGetClipboard) để lấy dữ liệu từ clipboard, nhưng tôi dường như không làm cho nó hoạt động trong C#/.NET.

Những thứ khác tôi đã thử là Clipboard WINAPI (OpenClipboard, GetClipboardData, CF_ENHMETAFILE), nhưng kết quả giống như sử dụng phiên bản .NET.

Vấn đề 2:

Sử dụng phạm vi và CopyPicture, nếu có bất kỳ hình ảnh trong bảng excel, những hình ảnh không được sao chép cùng với các dữ liệu xung quanh vào clipboard.

Một số mã nguồn

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(); 
} 

Bắt dữ liệu từ clipboard sử dụng WINAPI thành công, nhưng với chất lượng xấu. Nguồn:

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(); 
    } 
} 

Bất kỳ ai có giải pháp? (Tôi đang sử dụng .NET 2.0 btw)

+0

Bạn có thể chia sẻ mã nguồn của mình không? Bạn muốn lấy dữ liệu đã sao chép ở định dạng nào? Như bitmap? –

Trả lời

3

sẽ làm điều đó.

Bạn có thể thấy ASP.NET của chúng tôi (C# và VB) " Chart Excel và Range Imaging mẫu" mẫu here và tải về dùng thử miễn phí here nếu bạn muốn thử nó ra.

Bảng tínhGear cũng hoạt động với Windows Biểu mẫu, ứng dụng bảng điều khiển, v.v ... (bạn không chỉ định loại ứng dụng bạn đang tạo). Ngoài ra còn có một điều khiển Windows Forms để hiển thị một bảng tính trong ứng dụng của bạn nếu đó là những gì bạn đang thực sự sau.

Disclaimer: Tôi sở hữu SpreadsheetGear LLC

3

Từ những gì tôi hiểu được từ câu hỏi của bạn, tôi không thể tái tạo sự cố.

tôi chọn một loạt bằng tay trong Excel, chọn Copy As Hình với các tùy chọn như hiển thị trên màn hìnhBitmap chọn, sau đó tôi đã sử dụng đoạn mã sau để lưu dữ liệu vào clipboard:

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(); 
    } 
} 

Về vấn đề thứ hai của bạn: Theo như tôi biết, Excel không thể chọn cả dải ô và hình ảnh cùng một lúc. Nếu bạn muốn nhận được cả hai trong một hình ảnh cùng một lúc, bạn có thể phải in trang tính Excel sang tệp hình ảnh/PDF/XPS.

+0

Cảm ơn bạn đã cập nhật. Trong trường hợp của tôi, Clipboard.ContainsData (DataFormats.EnhancedMetafile) trả về true, nhưng Clipboard.GetData (DataFormats.EnhancedMetafile) sau đó luôn trả về null. mspaint.exe (như thường lệ) có thể dán dữ liệu được sao chép vào clipboard bằng CopyPicture() – Zurb

+0

@Zurb: Bạn có nhận 'null' từ' Clipboard.GetData' khi bạn chạy mã mẫu ở trên không ? Lưu ý rằng nó đang sử dụng lớp clipboard WPF trong 'System.Windows' và không phải là lớp từ Windows. Bạn có thể kiểm tra xem không ứng dụng nào khác đang khóa khay nhớ tạm không? –

+0

@divo: Tôi quên đề cập đến tôi đang sử dụng .NET 2.0, vì vậy tôi không thể chạy mã của bạn hoàn toàn như được đăng. Tôi đã thêm một số mã nguồn của mình vào câu hỏi. – Zurb

1

Vì asp.net thread không có ApartmentState quyền truy cập vào Clipboard Class, vì vậy bạn phải viết mã để truy cập vào Clipboard trong chủ đề mới. Ví dụ:

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

trong chủ đề chính:

.... 
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

Mã tôi đăng đã được triển khai để hoạt động trong Ứng dụng Biểu mẫu C# Windows chuẩn (vẫn không hoạt động btw), mặc dù tôi đã định sử dụng nó trên Web. Dù sao cũng cảm ơn. – Zurb

+0

Tôi có thể xác nhận rằng việc sử dụng tác phẩm này cho giải pháp web. – Dave

0

Điều thú vị là tôi đã làm điều này trong một khoang STA đối với một số trong khi với thành công. Tôi đã viết một ứng dụng chạy hàng tuần và gửi các báo cáo tình trạng dự án, bao gồm một số biểu đồ mà tôi tạo ra bằng cách sử dụng Excel.

Đêm qua, đồ thị này không trả về giá trị rỗng. Tôi đang gỡ lỗi ngày hôm nay và không tìm thấy lời giải thích chỉ là phương thức Clipboard.GetImage() trả về null bất ngờ mà nó không có. Bằng cách thiết lập một điểm ngắt tại cuộc gọi này, tôi có thể chứng minh một cách hiệu quả (bằng cách nhấn CTRL + V trong MS-Word) rằng hình ảnh IS thực sự trong clipboard. Alas tiếp tục trên Clipboard.GetImage() trả về null (cho dù tôi đang rình mò như thế này hay không).

Mã của tôi chạy dưới dạng ứng dụng bảng điều khiển và phương thức chính có thuộc tính [STAThread]. Tôi gỡ lỗi nó như là một cửa sổ ứng dụng hình thức (tất cả các mã của tôi là trong một thư viện và tôi chỉ đơn giản có hai kết thúc trước đó).

Cả hai đều trả về giá trị ngày hôm nay.

Không quan tâm Tôi tách trình tìm nạp biểu đồ thành một chuỗi như đã nêu (và lưu ý rằng thread.ApartmentState không còn được dùng nữa), và nó chạy ngọt, nhưng vẫn còn, null được trả về.

Vâng, tôi đã từ bỏ và làm những gì mà bất kỳ chuyên gia CNTT nào cũng sẽ làm, khởi động lại.

Thì đấy ... tất cả đều tốt. Đi con số ... là lý do tại sao tất cả chúng ta ghét máy tính, Microsoft Windows và Microsoft Office? Hmmmm ... Có một cái gì đó, một cái gì đó hoàn toàn thoáng qua có thể xảy ra với bạn PC mà làm cho Clipboard.GetImage() thất bại!

1

Đây là lỗi với GDI + khi chuyển đổi các siêu tệp thành định dạng bản đồ bit.
Điều này xảy ra với nhiều EMF hiển thị biểu đồ có văn bản. Để tạo lại, bạn chỉ cần tạo một biểu đồ trong excel hiển thị dữ liệu cho trục X và Y của nó. Sao chép biểu đồ dưới dạng ảnh và dán thành từ như một siêu tệp. Mở docx và bạn sẽ thấy một EMF trong thư mục media. Nếu bây giờ bạn mở EMF đó trong bất kỳ chương trình sơn nào dựa trên cửa sổ để chuyển đổi nó thành bitmap, bạn sẽ thấy sự biến dạng, đặc biệt, văn bản và dòng trở nên lớn hơn và bị méo. Thật không may, nó là một trong những vấn đề mà Microsoft không thể thừa nhận hoặc làm bất cứ điều gì về. Hãy hy vọng Google và Apple tiếp quản thế giới xử lý văn phòng/từ sớm.

0

Nếu bạn không nhớ Linux (kiểu), bạn có thể sử dụng OpenOffice (hoặc LibreOffice) để chuyển đổi xls đầu tiên sang pdf, sau đó sử dụng ImageMagic để chuyển đổi pdf sang hình ảnh. Hướng dẫn cơ bản có thể được tìm thấy tại http://www.novell.com/communities/node/5744/c-linux-thumbnail-generation-pdfdocpptxlsimages.

Ngoài ra, có vẻ như có .Net API cho cả hai chương trình được đề cập ở trên. Xem: http://www.opendocument4all.com/download/OpenOffice.net.pdfhttp://imagemagick.net/script/api.php#dot-net

Các vấn đề liên quan