Программно (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