Программно (C#) преобразование Excel в изображение

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

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

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

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

Проблема 1:

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

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

другие вещи, которые я пробовал, были буфером обмена 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)

6 ответов


SpreadsheetGear для .NET сделает это.

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

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

отказ от ответственности: я владею SpreadsheetGear LLC


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

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

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.


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


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

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

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

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

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

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

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

Ну, я сдался и сделал то, что сделал бы любой выродок, перезагрузился.

вуаля ... все было хорошо. Иди разберись ... вот почему мы все ненавидим компьютеры, Microsoft Windows и Microsoft Office? Хммм ... Есть что-то, что-то совершенно преходящее, что может случиться с вами ПК, который делает буфер обмена.Образец getimage() не получится!


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

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