xlsgen > overview > Pictures |
Pictures are a convenient type of object that adds up to the formatting of your Excel workbooks. A typical usage of pictures is company logos. The pictures are exposed in xlsgen the following way :
xlsgen supports all major picture file formats including :
Each Excel workbook supports one or more pictures in each worksheet. There is no known limitations in the amount of pictures you can add.
Pictures are added by embedding a blob inside the Excel file. Also, xlsgen factorizes pictures and stores the same picture only once, which helps keep the file low in size. xlsgen uses two mechanisms to avoid inserting the same picture twice : 1) the filepath 2) the actual content.
Each time a picture is inserted, you can accurately position it in any worksheet. The user is provided two models to position a picture, including :
Below is a drawing explaining how it works :
There are two separate programming interfaces to insert a picture. The main and richest one is through the Pictures collection. The other interfaces is through the NewPicture
method of the IXlsWorksheet object, as well as the NewPictureInPixels
method.
When inserting a picture, what's really only needed is a filename and the top-left corner where to insert it. When doing so, xlsgen inserts the picture at a normal scale and calculates the offsets with the Excel grid. If you'd like to put more constraints, you are free to use some of the options of the picture object including the ability to :
Here are two examples :
Java code |
// insert picture at B3 XlsPicture pic1 = wksht.getPictures().Add("picture.gif"); pic1.putLeftColumn(2); pic1.putTopCell(3); wksht.putLabel(3,6, "picture inserted at default size"); // insert picture at B15, double its size, unlocked aspect ratio XlsPicture pic2 = wksht.getPictures().Add("picture.gif"); pic2.putLeftColumn(2); pic2.putTopCell(15); pic2.putScaleWidth(200); pic2.putScaleHeight(200); pic2.putLockAspectRatio(false); wksht.putLabel(11,6, "picture inserted at twice its size"); wksht.putLabel(12,6, "with an unlocked aspect ratio when the user stretches it."); |
VB code |
' insert picture at B3 Dim pic1 As IXlsPicture Set pic1 = wksht.Pictures.Add("picture.gif") pic1.LeftColumn = 2 pic1.TopCell = 3 wksht.Label(3, 6) = "picture inserted at default size" ' insert picture at B15, double its size, unlocked aspect ratio Dim pic2 As IXlsPicture Set pic2 = wksht.Pictures.Add("picture.gif") pic2.LeftColumn = 2 pic2.TopCell = 15 pic2.ScaleWidth = 200 pic2.ScaleHeight = 200 pic2.LockAspectRatio = False wksht.Label(11, 6) = "picture inserted at twice its size" wksht.Label(12, 6) = "with an unlocked aspect ratio when the user stretches it." |
C# code |
// insert picture at B3 IXlsPicture pic1 = wksht.Pictures.Add("picture.gif"); pic1.LeftColumn = 2; pic1.TopCell = 3; wksht.set_Label(3,6, "picture inserted at default size"); // insert picture at B15, double its size, unlocked aspect ratio IXlsPicture pic2 = wksht.Pictures.Add("picture.gif"); pic2.LeftColumn = 2; pic2.TopCell = 15; pic2.ScaleWidth = 200; pic2.ScaleHeight = 200; pic2.LockAspectRatio = 0; wksht.set_Label(11,6, "picture inserted at twice its size"); wksht.set_Label(12,6, "with an unlocked aspect ratio when the user stretches it."); |
C/C++ code |
// insert picture at B3 xlsgen::IXlsPicturePtr pic1 = wksht->Pictures->Add(L"picture.gif"); pic1->LeftColumn = 2; pic1->TopCell = 3; wksht->Label[3][6] = L"picture inserted at default size"; // insert picture at B15, double its size, unlocked aspect ratio xlsgen::IXlsPicturePtr pic2 = wksht->Pictures->Add(L"picture.gif"); pic2->LeftColumn = 2; pic2->TopCell = 15; pic2->ScaleWidth = 200; pic2->ScaleHeight = 200; pic2->LockAspectRatio = FALSE; wksht->Label[11][6] = L"picture inserted at twice its size"; wksht->Label[12][6] = L"with an unlocked aspect ratio when the user stretches it."; |
(kept only for backwards compatibility reason)
Let's assume your top-left corner would be (r=4,c=3) and your bottom-right corner would be (r=9, c=4).
The following code snippets add a picture in the first worksheet.
VB code |
Dim wksht1 As IXlsWorksheet Set wksht1 = wbk.AddWorksheet("sheet1") ' add a picture from location C:\mypicture.jpeg ' top-left corner = (4,3) ' bottom-right corner = (9,4) ' topoffset = 0 (in 1440_ith units) ' leftoffset = 300 (in 1440_ith units) ' bottomoffset = 0 (in 1440_ith units) ' rightoffset = 600 (in 1440_ith units) wksht1.NewPicture("C:\mypicture.jpeg", 4,3, 9,4, 0,300,0,600) |
C# code |
IXlsWorksheet wksht1 = wbk.AddWorksheet( "sheet1" ); // work with this worksheet // add a picture from location C:\mypicture.jpeg // top-left corner = (4,3) // bottom-right corner = (9,4) // topoffset = 0 (in 1440_ith units) // leftoffset = 300 (in 1440_ith units) // bottomoffset = 0 (in 1440_ith units) // rightoffset = 600 (in 1440_ith units) wksht1.NewPicture(@"C:\mypicture.jpeg", 4,3, 9,4, 0,300,0,600); |
C/C++ code |
xlsgen::IXlsWorksheetPtr wksht1; wksht1 = wbk->AddWorksheet( L"sheet1" ); // add a picture from location C:\mypicture.jpeg // top-left corner = (4,3) // bottom-right corner = (9,4) // topoffset = 0 (in 1440_ith units) // leftoffset = 300 (in 1440_ith units) // bottomoffset = 0 (in 1440_ith units) // rightoffset = 600 (in 1440_ith units) wksht1->NewPicture("C:\\mypicture.jpeg", 4,3, 9,4, 0,300,0,600); |
The programming interface of worksheets is described here.
It is also possible to pass a memory buffer and a picture file type instead of a file, to accomodate scenarios where everything is done in memory. Here are examples how to do so in VB and C/C++ :
Java code |
// In this example, we read an existing file and make it a buffer in memory // for the sake of providing a full example, but it works as well if you have a buffer in memory // passed by some other function in your application. XlsEngine engine = new XlsEngine("./../../../xlsgen.dll"); XlsWorkbook workbook = engine.New("myfile.xls"); XlsWorksheet wksht = workbook.AddWorksheet("sheet1"); try { FileInputStream fis = new FileInputStream("IMG_2837.jpg"); int fileSize = fis.available(); byte[] bs = new byte[fileSize]; fis.read(bs); fis.close(); ' Insert the picture by passing : ' - the byte[] buffer ' - the picture type ' - the location (row1, col1, row2, col2) and offsets wksht.NewPictureInMemory(bs, xlsgen.picturetype_jpeg, 1,4, 6,8, 0,0,0,0); } catch(Exception e) { wksht.putLabel(1,2, "Memory picture insertion did not work"); } workbook.Close(); |
VB code |
' In this example, we read an existing file and make it a buffer in memory ' for the sake of providing a full example, but it works as well if you have a buffer in memory ' passed by some other function in your application. Public Sub Process() ' Read a picture IMG_2837.jpg Dim Line As String * 4096 Dim s As String Open "IMG_2837.jpg" For Binary Access Read As #1 Do While Not EOF(1) Get #1, , Line s = s & Line Loop Close #1 Dim buffer_input() As Byte ReDim buffer_input(Len(s)) For i = 1 To Len(s) buffer_input(i - 1) = Asc(Mid(s, i, 1)) Next ' Create an xlsgen engine instance Dim engine As CoXlsEngine Set engine = CreateObject("ExcelGenerator.ARsTDesign") Dim wbk As IXlsWorkbook Set wbk = engine.New("sample.xls") Dim wksht As IXlsWorksheet Set wksht = wbk.AddWorksheet("sheet1") ' Insert the picture by passing : ' - the byte array buffer ' - the picture type ' - the location (row1, col1, row2, col2) and offsets wksht.NewPictureInMemory buffer_input, picturetype_jpeg, 2, 2, 5, 4, 0, 0, 0, 0 ' Finalize the Excel file wbk.Close |
C# code |
// In this example, we read an existing file and make it a buffer in memory // for the sake of providing a full example, but it works as well if you have a buffer in memory // passed by some other function in your application. using System.IO; using System.Runtime.InteropServices; using xlsgen; // read a picture System.IO.FileStream fs = new System.IO.FileStream("IMG_2837.jpg", System.IO.FileMode.Open); int length = (int) fs.Length; byte[] fileBytes = new byte[length]; BinaryReader br = new BinaryReader(fs); br.Read(fileBytes, 0, length); br.Close(); fs.Close(); // create a simple Excel file and add a picture from a memory buffer xlsgen.CoXlsEngine engine = new xlsgen.CoXlsEngine(); xlsgen.IXlsWorkbook wbk = engine.New ("sample.xls"); xlsgen.IXlsWorksheet wksht = wbk.AddWorksheet("Sheet1"); wksht.NewPictureInMemory(fileBytes, xlsgen.enumPictureType.picturetype_jpeg, 2, 2, // top-left corner cell 5, 4, // bottom-right corner cell 0, //top offset (0 means aligned to grid) 0, //left offset (0 means aligned to grid) 0, //bottom offset (0 means aligned to grid) 0 //right offset (0 means aligned to grid) ); wbk.Close(); System.Runtime.InteropServices.Marshal.ReleaseComObject(engine); |
C/C++ code |
// In this example, we read an existing file and make it a buffer in memory // for the sake of providing a full example, but it works as well if you have a buffer in memory // passed by some other function in your application. xlsgen::IXlsEnginePtr engine( __uuidof(xlsgen::CoXlsEngine) ); xlsgen::IXlsWorkbookPtr wbk; wbk = engine->New( L"sample.xls" ); xlsgen::IXlsWorksheetPtr wksht = wbk->AddWorksheet("Sheet1"); FILE* f = fopen("IMG_2837.jpg", "rb"); // max 8KB for this buffer BYTE membuf[8192 + 1]; int membuflen = 0; DWORD dwRead; while ((dwRead = fread(membuf + membuflen, 1, 4096, f)) > 0) membuflen += dwRead; fclose(f); SAFEARRAY* pSA = ::SafeArrayCreateVector(VT_UI1, 0, membuflen); BYTE HUGEP *plong = NULL; ::SafeArrayAccessData(pSA, (void HUGEP**)&plong); if (plong) { memcpy(plong, membuf, membuflen); ::SafeArrayUnaccessData(pSA); VARIANT vinput; vinput.vt = VT_ARRAY | VT_UI1; vinput.parray = pSA; _variant_t v(vinput); wksht->NewPictureInMemory(v, xlsgen::picturetype_jpeg, 2, 2, /* top-left corner cell*/ 5, 4, /* bottom-right corner cell*/ 0, /*top offset (0 means aligned to grid)*/ 0, /*left offset (0 means aligned to grid)*/ 0, /*bottom offset (0 means aligned to grid)*/ 0 /*right offset (0 means aligned to grid)*/); } ::SafeArrayDestroy(pSA); wbk->Close(); |
The same method call used for inserting pictures from a hard drive can actually be used to specify a url where a picture can be downloaded from.
The url needs not specify an image file by itself, nor be a static url. xlsgen invokes the http request on your behalf, and our engine reads the response headers in order to know what is the content type of the picture being downloaded (for instance image/gif represents a GIF picture). This is transparent to the client application and the web server hosting the picture.
C/C++ code |
xlsgen::IXlsPicturePtr pic1 = worksheet->Pictures->Add(L"http://xlsgen.arstdesign.com/demo0.gif"); pic1->TopCell = 2; pic1->LeftColumn = 2; pic1->Borders->Style = xlsgen::chartborderstyle_solid; pic1->Borders->Color = xlsgen::colorRed; |
Developers using the regular Windows graphics API know that a bitmap is stored as a HBITMAP. Since this feature is independent of which programming language you are using, the Windows API being exposed to each programming language, it is of particular interest to make it possible to insert a picture with xlsgen using a simple HBITMAP as parameter. That is what the following piece of C/C++ code does :
// build a HBITMAP by loading a bitmap in memory HBITMAP hBmp = (HBITMAP) ::LoadImage(hInstance, "350_original.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE); // create a simple spreadsheet xlsgen::IXlsEnginePtr engine( __uuidof(xlsgen::CoXlsEngine) ); xlsgen::IXlsWorkbookPtr wbk = engine->New( L"sample.xls" ); xlsgen::IXlsWorksheetPtr wksht = wbk->AddWorksheet( L"samplesheet" ); // insert the picture by passing the HBITMAP AddPicture(wksht, hBmp, 5/*top*/, 2/*left*/, 15/*bottom*/, 6/*right*/); wbk->Close(); void AddPicture(xlsgen::IXlsWorksheetPtr wksht, HBITMAP hBmp, int top, int left, int bottom, int right) { HDC hdcScreen = CreateDC("DISPLAY", NULL, NULL, NULL); HDC hdcCompatible = CreateCompatibleDC(hdcScreen); HBITMAP hOldBmp = (HBITMAP) ::SelectObject(hdcCompatible, hBmp); PBITMAPINFO pbi; { // Retrieve the bitmap color format, width, and height. BITMAP bmp; GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp); // Convert the color format to a count of bits. WORD cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); if (cClrBits == 1) cClrBits = 1; else if (cClrBits <= 4) cClrBits = 4; else if (cClrBits <= 8) cClrBits = 8; else if (cClrBits <= 16) cClrBits = 16; else if (cClrBits <= 24) cClrBits = 24; else cClrBits = 32; // Allocate memory for the BITMAPINFO structure. (This structure // contains a BITMAPINFOHEADER structure and an array of RGBQUAD // data structures.) if (cClrBits != 24) pbi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits)); // There is no RGBQUAD array for the 24-bit-per-pixel format. else pbi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)); // Initialize the fields in the BITMAPINFO structure. pbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbi->bmiHeader.biWidth = bmp.bmWidth; pbi->bmiHeader.biHeight = bmp.bmHeight; pbi->bmiHeader.biPlanes = bmp.bmPlanes; pbi->bmiHeader.biBitCount = bmp.bmBitsPixel; if (cClrBits < 24) pbi->bmiHeader.biClrUsed = (1 << cClrBits); // If the bitmap is not compressed, set the BI_RGB flag. pbi->bmiHeader.biCompression = BI_RGB; // Compute the number of bytes in the array of color // indices and store the result in biSizeImage. // For Windows NT, the width must be DWORD aligned unless // the bitmap is RLE compressed. This example shows this. // For Windows 95/98/Me, the width must be WORD aligned unless the // bitmap is RLE compressed. pbi->bmiHeader.biSizeImage = ((pbi->bmiHeader.biWidth * cClrBits +31) & ~31) /8 * pbi->bmiHeader.biHeight; // Set biClrImportant to 0, indicating that all of the // device colors are important. pbi->bmiHeader.biClrImportant = 0; } PBITMAPINFOHEADER pbih = (PBITMAPINFOHEADER) pbi; LPBYTE lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); int nbScanlines = GetDIBits(hdcCompatible, hBmp, 0, (WORD) pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS); BITMAPFILEHEADER hdr; // bitmap file-header int membufferlen = sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage; SAFEARRAY* pSA = ::SafeArrayCreateVector(VT_UI1, 0, membufferlen); BYTE HUGEP* plong = NULL; ::SafeArrayAccessData(pSA, (void HUGEP**)&plong); if (plong) { hdr.bfType = 0x4d42; // 0x42 = "B" 0x4d = "M" hdr.bfSize = (DWORD) membufferlen; hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; // Compute the offset to the array of color indices. hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD); // Copy the BITMAPFILEHEADER into the .BMP file. memcpy(plong, &hdr, sizeof(BITMAPFILEHEADER)); // Copy the BITMAPINFOHEADER and RGBQUAD array into the file. memcpy(plong + sizeof(BITMAPFILEHEADER), pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD)); // Copy the array of color indices into the .BMP file. DWORD cb = pbih->biSizeImage; memcpy(plong + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), lpBits, cb); ::SafeArrayUnaccessData(pSA); VARIANT vinput; vinput.vt = VT_ARRAY | VT_UI1; vinput.parray = pSA; _variant_t v(vinput); wksht->NewPictureInMemory(v, xlsgen::picturetype_bmp, top, left, bottom, right, 0, /*top offset (0 means aligned to grid)*/ 0, /*left offset (0 means aligned to grid)*/ 0, /*bottom offset (0 means aligned to grid)*/ 0 /*right offset (0 means aligned to grid)*/); } ::SafeArrayDestroy(pSA); ::SelectObject(hdcCompatible, hOldBmp); ::DeleteDC(hdcCompatible); ::DeleteDC(hdcScreen); }
Other than adding pictures, xlsgen has an introspection programming interface which lets you query the position and size of any existing picture in a file.
Note that, because the same programming interface actually works with other objects such as vector drawings and charts, the main name of the programming interface is Shapes, not Pictures.
In the following scenario, what we do is get the position and size of an existing shape, then insert a picture on top of that shape.
VB code |
Dim wbk As IXlsWorkbook wbk = engine.Open("Book1_existingshapes.xls", "output.xls") Dim wksht As IXlsWorksheet wksht = wbk.WorksheetByIndex(2) Dim shape As IXlsShape shape = wksht.Shapes.Item(1) Dim nameShape As String nameShape = shape.Name Dim cellLeft Dim cellRight Dim cellTop Dim cellBottom cellLeft = shape.LeftColumn cellRight = shape.RightColumn cellTop = shape.TopCell cellBottom = shape.BottomCell Dim cellLeftOffset Dim cellRightOffset Dim cellTopOffset Dim cellBottomOffset cellLeftOffset = shape.LeftOffset cellRightOffset = shape.RightOffset cellTopOffset = shape.TopOffset cellBottomOffset = shape.BottomOffset ' insert picture databar1.jpg using above information wksht.NewPicture("databar1.jpg", _ cellTop, cellLeft, _ cellBottom - 1, cellRight - 1, _ cellTopOffset, cellLeftOffset, _ -cellBottomOffset, -cellRightOffset) wbk.Close() |
C/C++ code |
xlsgen::IXlsWorkbookPtr wbk = engine->Open(L"Book1_existingshapes.xls", L"output.xls"); xlsgen::IXlsWorksheetPtr wksht = wbk->WorksheetByIndex[2]; xlsgen::IXlsShapePtr shape = wksht->Shapes->Item[1]; _bstr_t nameShape = shape->Name; int cellLeft = shape->LeftColumn; int cellRight = shape->RightColumn; int cellTop = shape->TopCell; int cellBottom = shape->BottomCell; int cellLeftOffset = shape->LeftOffset; int cellRightOffset = shape->RightOffset; int cellTopOffset = shape->TopOffset; int cellBottomOffset = shape->BottomOffset; // insert picture databar1.jpg using above information wksht->NewPicture(L"databar1.jpg", cellTop, cellLeft, cellBottom > 1 ? (cellBottom - 1) : 1, cellRight > 1 ? (cellRight - 1) : 1, cellTopOffset, cellLeftOffset, cellBottom > 1 ? - cellBottomOffset : (1024 - cellBottomOffset), cellRight > 1 ? - cellRightOffset : (1024 - cellRightOffset)); wbk->Close(); |
It is possible to delete one or more pictures in an existing file. The Shape object supports a Delete
method.
Thanks to the fact that Shapes are an abstraction for pictures, vector drawings, charts, visual components, ...it is possible to use the same method in order to delete those pictures, vector drawings, charts, visual components, ...
Any picture in existing Excel files can be extracted. The extraction is either directly written to a file, or to a memory block.
It is possible to change the display order of any picture or shape. The Shape object supports a BringToFront
method. By iterating this mechanism, it is possible to redefine the display order of any picture or shape (shape are pictures, vector drawings, charts, visual components, ...).
C/C++ code |
xlsgen::IXlsPicturePtr picture1 = worksheet->Pictures->Add( L"input\\a.gif" ); picture1->LeftColumn = 1; picture1->TopCell = 1; picture1->ChromaKeyColor = 0xFF0000; // RED chroma key |
xlsgen documentation. © ARsT Design all rights reserved.