Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f2f6281

Browse files
Merge pull request #1 from DevExpress-Examples/main
Main
2 parents 759ef55 + ee3b4ab commit f2f6281

13 files changed

+511
-0
lines changed

‎CS/.dockerignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
**/.classpath
2+
**/.dockerignore
3+
**/.env
4+
**/.git
5+
**/.gitignore
6+
**/.project
7+
**/.settings
8+
**/.toolstarget
9+
**/.vs
10+
**/.vscode
11+
**/*.*proj.user
12+
**/*.dbmdl
13+
**/*.jfm
14+
**/azds.yaml
15+
**/bin
16+
**/charts
17+
**/docker-compose*
18+
**/Dockerfile*
19+
**/node_modules
20+
**/npm-debug.log
21+
**/obj
22+
**/secrets.dev.yaml
23+
**/values.dev.yaml
24+
LICENSE
25+
README.md

‎CS/BusinessObjects/FileFormat.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
namespace RichEditOpenAIWebApi.BusinessObjects
2+
{
3+
public enum RichEditFormat
4+
{
5+
Txt = 1,
6+
Rtf = 2,
7+
Html = 3,
8+
Docx = 4,
9+
Mht = 5,
10+
Xml = 6, // WordML
11+
Odt = 7,
12+
ePub = 9,
13+
Doc = 10,
14+
Pdf = 11,
15+
Docm = 12,
16+
Dotx = 13,
17+
Dotm = 14,
18+
Dot = 15,
19+
}
20+
21+
public enum SpreadsheetFormat
22+
{
23+
Xls = 1,
24+
Xlsx = 2,
25+
Html = 6,
26+
Xlsm = 7,
27+
Xlt = 8,
28+
Xltx = 9,
29+
Xltm = 10,
30+
Xlsb = 11,
31+
XmlSpreadsheet2003 = 12,
32+
Pdf = 13,
33+
}
34+
35+
}

‎CS/BusinessObjects/Helpers.cs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
using DevExpress.Spreadsheet;
2+
using DevExpress.XtraRichEdit;
3+
using RichEditDocumentFormat = DevExpress.XtraRichEdit.DocumentFormat;
4+
using RichEditEncryptionSettings = DevExpress.XtraRichEdit.API.Native.EncryptionSettings;
5+
using SpreadsheetDocumentFormat = DevExpress.Spreadsheet.DocumentFormat;
6+
using SpreadsheetEncryptionSettings = DevExpress.Spreadsheet.EncryptionSettings;
7+
8+
namespace RichEditOpenAIWebApi.BusinessObjects
9+
{
10+
public static class RichEditHelper
11+
{
12+
public static async Task LoadFile(RichEditDocumentServer server, IFormFile file)
13+
{
14+
using (var stream = new MemoryStream())
15+
{
16+
await file.CopyToAsync(stream);
17+
stream.Seek(0, SeekOrigin.Begin);
18+
server.LoadDocument(stream);
19+
}
20+
}
21+
public static Stream SaveDocument(RichEditDocumentServer server, RichEditFormat outputFormat, RichEditEncryptionSettings? encryptionSettings = null)
22+
{
23+
MemoryStream resultStream = new MemoryStream();
24+
if (outputFormat == RichEditFormat.Pdf)
25+
server.ExportToPdf(resultStream);
26+
else
27+
{
28+
RichEditDocumentFormat documentFormat = new RichEditDocumentFormat((int)outputFormat);
29+
if (documentFormat == RichEditDocumentFormat.Html)
30+
server.Options.Export.Html.EmbedImages = true;
31+
if (encryptionSettings == null)
32+
server.SaveDocument(resultStream, documentFormat);
33+
else
34+
server.SaveDocument(resultStream, documentFormat, encryptionSettings);
35+
}
36+
resultStream.Seek(0, SeekOrigin.Begin);
37+
return resultStream;
38+
}
39+
public static string GetContentType(RichEditFormat documentFormat)
40+
{
41+
switch (documentFormat)
42+
{
43+
case RichEditFormat.Doc:
44+
case RichEditFormat.Dot:
45+
return "application/msword";
46+
case RichEditFormat.Docm:
47+
case RichEditFormat.Docx:
48+
case RichEditFormat.Dotm:
49+
case RichEditFormat.Dotx:
50+
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
51+
case RichEditFormat.ePub:
52+
return "application/epub+zip";
53+
case RichEditFormat.Mht:
54+
case RichEditFormat.Html:
55+
return "text/html";
56+
case RichEditFormat.Odt:
57+
return "application/vnd.oasis.opendocument.text";
58+
case RichEditFormat.Txt:
59+
return "text/plain";
60+
case RichEditFormat.Rtf:
61+
return "application/rtf";
62+
case RichEditFormat.Xml:
63+
return "application/xml";
64+
case RichEditFormat.Pdf:
65+
return "application/pdf";
66+
default: return string.Empty;
67+
}
68+
}
69+
public static async Task<string> FileToBase64String(IFormFile file)
70+
{
71+
using (var stream = new MemoryStream())
72+
{
73+
await file.CopyToAsync(stream);
74+
stream.Seek(0, SeekOrigin.Begin);
75+
byte[] imageBytes = stream.ToArray();
76+
return Convert.ToBase64String(imageBytes);
77+
}
78+
}
79+
}
80+
public static class SpreadsheetHelper
81+
{
82+
83+
public static async Task LoadWorkbook(Workbook workbook, IFormFile file)
84+
{
85+
using (var stream = new MemoryStream())
86+
{
87+
await file.CopyToAsync(stream);
88+
stream.Seek(0, SeekOrigin.Begin);
89+
workbook.LoadDocument(stream);
90+
}
91+
}
92+
93+
public static Stream SaveDocument(Workbook workbook, SpreadsheetFormat outputFormat, SpreadsheetEncryptionSettings? encryptionSettings = null)
94+
{
95+
MemoryStream resultStream = new MemoryStream();
96+
97+
SpreadsheetDocumentFormat documentFormat = new SpreadsheetDocumentFormat((int)outputFormat);
98+
if (outputFormat == SpreadsheetFormat.Pdf)
99+
workbook.ExportToPdf(resultStream);
100+
else
101+
{
102+
103+
if (encryptionSettings == null)
104+
workbook.SaveDocument(resultStream, documentFormat);
105+
else
106+
workbook.SaveDocument(resultStream, documentFormat, encryptionSettings);
107+
}
108+
109+
resultStream.Seek(0, SeekOrigin.Begin);
110+
return resultStream;
111+
}
112+
public static string GetContentType(SpreadsheetFormat documentFormat)
113+
{
114+
switch (documentFormat)
115+
{
116+
case SpreadsheetFormat.Xls:
117+
case SpreadsheetFormat.Xlt:
118+
return "application/msword";
119+
case SpreadsheetFormat.Xlsx:
120+
case SpreadsheetFormat.Xlsb:
121+
case SpreadsheetFormat.Xlsm:
122+
case SpreadsheetFormat.Xltm:
123+
return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
124+
case SpreadsheetFormat.Html:
125+
return "text/html";
126+
case SpreadsheetFormat.XmlSpreadsheet2003:
127+
return "application/xml";
128+
case SpreadsheetFormat.Pdf:
129+
return "application/pdf";
130+
default: return string.Empty;
131+
}
132+
133+
134+
}
135+
}
136+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
using Azure;
2+
using Azure.AI.OpenAI;
3+
using DevExpress.Drawing;
4+
using DevExpress.Office.Utils;
5+
6+
namespace RichEditOpenAIWebApi.BusinessObjects {
7+
class OpenAIClientImageHelper {
8+
OpenAIClient client;
9+
internal OpenAIClientImageHelper(string openAIApiKey) {
10+
client = new OpenAIClient(openAIApiKey, new OpenAIClientOptions());
11+
}
12+
string ConvertDXImageToBase64String(DXImage image) {
13+
using (MemoryStream stream = new MemoryStream()) {
14+
image.Save(stream, DXImageFormat.Png);
15+
byte[] imageBytes = stream.ToArray();
16+
return Convert.ToBase64String(imageBytes);
17+
}
18+
}
19+
internal async Task<string> DescribeImageAsync(OfficeImage image) {
20+
string base64Content = ConvertDXImageToBase64String(image.DXImage);
21+
string imageContentType = OfficeImage.GetContentType(OfficeImageFormat.Png);
22+
return await GetImageDescription($"data:{imageContentType};base64,{base64Content}");
23+
}
24+
internal async Task<string> GetImageDescription(string uriString) {
25+
ChatCompletionsOptions chatCompletionsOptions = new() {
26+
DeploymentName = "gpt-4-vision-preview",
27+
Messages =
28+
{
29+
new ChatRequestSystemMessage("You are a helpful assistant that describes images."),
30+
new ChatRequestUserMessage(
31+
new ChatMessageTextContentItem("Give a description of this image in no more than 10 words"),
32+
new ChatMessageImageContentItem(new Uri(uriString))),
33+
},
34+
MaxTokens = 300
35+
};
36+
37+
Response<ChatCompletions> chatResponse = await client.GetChatCompletionsAsync(chatCompletionsOptions);
38+
ChatChoice choice = chatResponse.Value.Choices[0];
39+
return choice.Message.Content;
40+
}
41+
42+
}
43+
}

‎CS/BusinessObjects/RichEditExtension.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using DevExpress.XtraRichEdit;
2+
using DevExpress.XtraRichEdit.API.Native;
3+
4+
namespace RichEditOpenAIWebApi.BusinessObjects {
5+
static class RichEditExtension {
6+
internal static void GenerateAltTextForImages(this IRichEditDocumentServer server, Action<SubDocument> action) {
7+
Document document = server.Document;
8+
UpdateSubDocument(document, action);
9+
foreach (var section in document.Sections) {
10+
UpdateSectionHeaderFooter(section, HeaderFooterType.Primary, action);
11+
UpdateSectionHeaderFooter(section, HeaderFooterType.First, action);
12+
UpdateSectionHeaderFooter(section, HeaderFooterType.Odd, action);
13+
UpdateSectionHeaderFooter(section, HeaderFooterType.Even, action);
14+
}
15+
}
16+
static void UpdateSectionHeaderFooter(Section section, HeaderFooterType type, Action<SubDocument> action) {
17+
if (section.HasHeader(type)) {
18+
SubDocument header = section.BeginUpdateHeader(type);
19+
UpdateSubDocument(header, action);
20+
section.EndUpdateHeader(header);
21+
}
22+
if (section.HasFooter(type)) {
23+
SubDocument footer = section.BeginUpdateFooter(type);
24+
UpdateSubDocument(footer, action);
25+
section.EndUpdateFooter(footer);
26+
}
27+
}
28+
static void UpdateSubDocument(SubDocument document, Action<SubDocument> action) {
29+
document.BeginUpdate();
30+
action(document);
31+
document.EndUpdate();
32+
}
33+
}
34+
}

‎CS/Controllers/OpenAIController.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using DevExpress.Office.Utils;
2+
using DevExpress.Spreadsheet;
3+
using DevExpress.XtraRichEdit;
4+
using Microsoft.AspNetCore.Mvc;
5+
using RichEditOpenAIWebApi.BusinessObjects;
6+
using Swashbuckle.AspNetCore.Annotations;
7+
using System.Net;
8+
9+
namespace RichEditOpenAIWebApi.Controllers
10+
{
11+
[ApiController]
12+
[Route("[controller]/[action]")]
13+
public class OpenAIController : ControllerBase
14+
{
15+
string openAIApiKey = "";
16+
[HttpPost]
17+
[SwaggerResponse((int)HttpStatusCode.OK, "Download a file", typeof(FileContentResult))]
18+
public async Task<IActionResult> GenerateImageAltText(IFormFile documentWithImage, [FromQuery] RichEditFormat outputFormat)
19+
{
20+
try
21+
{
22+
var imageDescriber = new OpenAIClientImageHelper(openAIApiKey);
23+
using (var server = new RichEditDocumentServer())
24+
{
25+
await RichEditHelper.LoadFile(server, documentWithImage);
26+
27+
server.GenerateAltTextForImages((document) =>
28+
{
29+
foreach (var shape in document.Shapes)
30+
{
31+
if (shape.Type == DevExpress.XtraRichEdit.API.Native.ShapeType.Picture && string.IsNullOrEmpty(shape.AltText))
32+
shape.AltText = imageDescriber.DescribeImageAsync(shape.PictureFormat.Picture).Result;
33+
}
34+
});
35+
36+
Stream result = RichEditHelper.SaveDocument(server, outputFormat);
37+
string contentType = RichEditHelper.GetContentType(outputFormat);
38+
string outputStringFormat = outputFormat.ToString().ToLower();
39+
return File(result, contentType, $"result.{outputStringFormat}");
40+
}
41+
}
42+
catch (Exception e)
43+
{
44+
return StatusCode(500, e.Message + Environment.NewLine + e.StackTrace);
45+
}
46+
}
47+
48+
[HttpPost]
49+
[SwaggerResponse((int)HttpStatusCode.OK, "Download a file", typeof(FileContentResult))]
50+
public async Task<IActionResult> GenerateChartAltText(IFormFile documentWithImage, [FromQuery] SpreadsheetFormat outputFormat)
51+
{
52+
try
53+
{
54+
var imageDescriber = new OpenAIClientImageHelper(openAIApiKey);
55+
using (var workbook = new Workbook())
56+
{
57+
await SpreadsheetHelper.LoadWorkbook(workbook, documentWithImage);
58+
59+
foreach (var worksheet in workbook.Worksheets)
60+
{
61+
foreach (var chart in worksheet.Charts)
62+
{
63+
OfficeImage image = chart.ExportToImage();
64+
chart.AlternativeText = await imageDescriber.DescribeImageAsync(image);
65+
}
66+
}
67+
68+
Stream result = SpreadsheetHelper.SaveDocument(workbook, outputFormat);
69+
string contentType = SpreadsheetHelper.GetContentType(outputFormat);
70+
string outputStringFormat = outputFormat.ToString().ToLower();
71+
return File(result, contentType, $"result.{outputStringFormat}");
72+
}
73+
}
74+
catch (Exception e)
75+
{
76+
return StatusCode(500, e.Message + Environment.NewLine + e.StackTrace);
77+
}
78+
79+
}
80+
}
81+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /