2009-08-17 2 views
10

Я хочу преобразовать файл excel в изображение (каждый формат в порядке) программно (C#). В настоящее время я использую Microsoft Interop Libraries & Office 2007, но он не поддерживает сохранение изображения по умолчанию.Программно (C#) конвертировать Excel в изображение

Так что моя текущая работа вокруг выглядит следующим образом:

  • Открыть файл Excel с помощью Microsoft Interop;
  • Узнайте максимальный диапазон (содержащий данные);
  • Используйте CopyPicture() в этом диапазоне, который скопирует данные в буфер обмена.

Теперь сложная часть (и мои проблемы):

Задача 1:

Использование класса .NET Clipboard, я не в состоянии получить EXACT скопированные данные из буфера обмена : данные одинаковые, но каким-то образом форматирование искажается (шрифт всего документа кажется полужирным и немного более нечитаемым, пока они не были); Если я вставлю из буфера обмена с помощью mspaint.exe, вставленное изображение будет правильным (и так же, как я хочу, чтобы оно было).

Я разобрал mspaint.exe и нашел функцию, которую он использует (OleGetClipboard), чтобы получить данные из буфера обмена, но я не могу заставить ее работать в C#/.NET.

Другие вещи, которые я пробовал, были Clipboard WINAPI (OpenClipboard, GetClipboardData, CF_ENHMETAFILE), но результаты были такими же, как и с использованием версий .NET.

Проблема 2:

Используя диапазон и CopyPicture, если есть какие-либо изображения в листе Excel, эти изображения не копируются вместе с окружающими данные в буфер обмена.

Некоторые из исходного кода

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

Получение данных из буфера обмена с помощью WINAPI успешно, но с плохим качеством. Источник:

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

У кого-нибудь есть решение? (Я использую .NET 2.0 btw)

+0

Не могли бы вы поделиться исходным кодом? В каком формате вы хотите получить скопированные данные? Как растровое изображение? –

ответ

3

будет делать это.

Вы можете увидеть наши ASP.NET (C# и VB) « Excel диаграммы и диапазон обработки изображений Образцы» образцы here и скачать бесплатную пробную версию here если вы хотите попробовать его.

SpreadsheetGear также работает с Windows Forms, консольными приложениями и т. Д. (Вы не указали, какой тип приложения вы создаете). Существует также элемент управления Windows Forms для отображения книги в вашем приложении, если это то, что вы действительно после.

Отказ от ответственности: У меня есть SpreadsheetGear ООО

3

Из того, что я понимаю из вашего вопроса, я не могу воспроизвести проблему.

Я выбрал диапазон вручную в Excel, выбрал Копировать как изображение с параметрами , как показано на экране и Bitmap выбран, то я использовал следующий код для сохранения данных из буфера обмена:

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

Что касается вашей второй проблемы: насколько я знаю, в Excel невозможно использовать одновременно диапазон ячеек и изображение. Если вы хотите получить оба изображения одновременно, вам, возможно, придется распечатать лист Excel в файле image/PDF/XPS.

+0

Благодарим за обновление. В моем случае Clipboard.ContainsData (DataFormats.EnhancedMetafile) возвращает true, но Clipboard.GetData (DataFormats.EnhancedMetafile) впоследствии всегда возвращает null. mspaint.exe (как всегда) может вставлять данные, которые копируются в буфер обмена CopyPicture() – Zurb

+0

@Zurb: вы получаете 'null' из' Clipboard.GetData', а также при запуске кода примера выше ? Обратите внимание, что он использует класс буфера обмена WPF в 'System.Windows', а не тот, который из форм Windows. Можете ли вы проверить, что никакое другое приложение не блокирует буфер обмена? –

+0

@divo: Я забыл упомянуть, что я использую .NET 2.0, поэтому я не могу полностью запустить ваш код, как опубликовано. Я добавил некоторые из моих исходников на вопрос. – Zurb

1

Поскольку asp.net поток не имеет правильный ApartmentState доступа к Clipboard класса, так что вы должны написать код, чтобы получить доступ к буферу обмена в новом потоке. Например:

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

в основном потоке:

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

Код, который я опубликовал, был реализован для работы в стандартном приложении C# для Windows Form (все еще не работающем кстати), хотя я планировал использовать его в Интернете. В любом случае спасибо. – Zurb

+0

Я могу подтвердить, что это работает для веб-решения. – Dave

0

Интересно, я делал это в купе STA некоторое время с успехом. Я написал приложение, которое работает на еженедельной основе и отправляет отчеты о статусе проекта, включая некоторые графики, которые я генерирую программно с помощью Excel.

Прошлой ночью это не привело к тому, что все графы были возвращены null. Я отлаживаю сегодня и не нахожу объяснений, что метод Clipboard.GetImage() возвращает null внезапно, чего не было. Установив точку останова при этом вызове, я могу эффективно продемонстрировать (нажав CTRL + V в MS-Word), что изображение действительно находится в буфере обмена. Увы, продолжение в Clipboard.GetImage() возвращает значение null (независимо от того, как я это просматриваю или нет).

Мой код работает как консольное приложение, а метод Main имеет атрибут [STAThread]. Я отлаживаю его как приложение Windows Form (весь мой код находится в библиотеке, и у меня просто есть два передних конца).

Оба возвращают null сегодня.

Из интереса я выделил сборщик диаграммы в поток, как указано (и обратите внимание, что thread.ApartmentState устарел), и он запускается сладко, но все же, nulls возвращаются.

Ну, я сдался и сделал то, что сделал бы любой ИТ-специалист, перезагрузился.

Voila ... все было хорошо. Идите по фигуре ... вот почему мы все ненавидим компьютеры, Microsoft Windows и Microsoft Office? Хмммм ... Что-то, что-то совершенно преходящее, что может случиться с вами, что делает Clipboard.GetImage() неудачным!

1

Это ошибка с GDI +, когда дело доходит до преобразования метафайлов в формат бит-карты.
Это происходит для многих EMF, которые отображают диаграммы с текстами. Чтобы воссоздать, вам просто нужно создать диаграмму в excel, которая отображает данные для его оси X и Y. Скопируйте диаграмму как изображение и вставьте слово в качестве метафайла. Откройте docx, и вы увидите EMF в папке с медиа. Если теперь вы откроете эту EMF в любой программе на основе окон, которая преобразует ее в растровое изображение, вы увидите искажения, в частности, текст и линии станут больше и искажены. К сожалению, это одна из тех проблем, которые Microsoft вряд ли признает или что-то предпримет. Будем надеяться, что Google и Apple вскоре перейдут в мир офисной/текстовой обработки.

0

Если вы не против Linux (стиль), вы можете использовать OpenOffice (или LibreOffice) для преобразования xls сначала в pdf, а затем использовать ImageMagic для преобразования PDF в изображение. Основное руководство можно найти по адресу http://www.novell.com/communities/node/5744/c-linux-thumbnail-generation-pdfdocpptxlsimages.

Также, как представляется, API-интерфейсы .Net для обеих упомянутых выше программ. См: http://www.opendocument4all.com/download/OpenOffice.net.pdf и http://imagemagick.net/script/api.php#dot-net

Смежные вопросы