#c# #image #openxml #docx
#c# #изображение #openxml #docx
Вопрос:
Я использую OpenXML для вставки изображения в мой документ. Код, предоставленный Microsoft, работает, но изображение становится намного меньше:
public static void InsertAPicture(string document, string fileName)
{
using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(document, true))
{
MainDocumentPart mainPart = wordprocessingDocument.MainDocumentPart;
ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Jpeg);
using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
imagePart.FeedData(stream);
}
AddImageToBody(wordprocessingDocument, mainPart.GetIdOfPart(imagePart));
}
}
private static void AddImageToBody(WordprocessingDocument wordDoc, string relationshipId)
{
// Define the reference of the image.
var element =
new Drawing(
new DW.Inline(
new DW.Extent() { Cx = 990000L, Cy = 792000L },
new DW.EffectExtent()
{
LeftEdge = 0L,
TopEdge = 0L,
RightEdge = 0L,
BottomEdge = 0L
},
new DW.DocProperties()
{
Id = (UInt32Value)1U,
Name = "Picture 1"
},
new DW.NonVisualGraphicFrameDrawingProperties(
new A.GraphicFrameLocks() { NoChangeAspect = true }),
new A.Graphic(
new A.GraphicData(
new PIC.Picture(
new PIC.NonVisualPictureProperties(
new PIC.NonVisualDrawingProperties()
{
Id = (UInt32Value)0U,
Name = "New Bitmap Image.jpg"
},
new PIC.NonVisualPictureDrawingProperties()),
new PIC.BlipFill(
new A.Blip(
new A.BlipExtensionList(
new A.BlipExtension()
{
Uri =
"{28A0092B-C50C-407E-A947-70E740481C1C}"
})
)
{
Embed = relationshipId,
CompressionState = A.BlipCompressionValues.Print
},
new A.Stretch(
new A.FillRectangle())),
new PIC.ShapeProperties(
new A.Transform2D(
new A.Offset() { X = 0L, Y = 0L },
new A.Extents() { Cx = 990000L, Cy = 792000L }),
new A.PresetGeometry(
new A.AdjustValueList()
) { Preset = A.ShapeTypeValues.Rectangle }))
) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
)
{
DistanceFromTop = (UInt32Value)0U,
DistanceFromBottom = (UInt32Value)0U,
DistanceFromLeft = (UInt32Value)0U,
DistanceFromRight = (UInt32Value)0U,
EditId = "50D07946"
});
// Append the reference to body, the element should be in a Run.
wordDoc.MainDocumentPart.Document.Body.AppendChild(new Paragraph(new Run(element)));
}
Мне нужно вернуть изображению первоначальный размер. Как я могу это сделать? (Я погуглил, как это сделать вне этого процесса, но это не то, что я ищу. Я должен предположить, что внутри данного кода есть какие-то свойства размера).
Редактировать: Обновленный код (все еще не работает)
public static void InsertAPicture(string document, string fileName)
{
using (WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(document, true))
{
MainDocumentPart mainPart = wordprocessingDocument.MainDocumentPart;
ImagePart imagePart = mainPart.AddImagePart(ImagePartType.Jpeg);
using (FileStream stream = new FileStream(fileName, FileMode.Open))
{
imagePart.FeedData(stream);
}
AddImageToBody(wordprocessingDocument, mainPart.GetIdOfPart(imagePart), fileName);
}
}
private static void AddImageToBody(WordprocessingDocument wordDoc, string relationshipId, string fileName)
{
var img = new BitmapImage(new Uri(fileName, UriKind.RelativeOrAbsolute));
var widthPx = img.PixelWidth;
var heightPx = img.PixelHeight;
var horzRezDpi = img.DpiX;
var vertRezDpi = img.DpiY;
const int emusPerInch = 914400;
const int emusPerCm = 360000;
var maxWidthCm = 16.51;
var widthEmus = (long)(widthPx / horzRezDpi * emusPerInch);
var heightEmus = (long)(heightPx / vertRezDpi * emusPerInch);
var maxWidthEmus = (long)(maxWidthCm * emusPerCm);
if (widthEmus > maxWidthEmus)
{
var ratio = (heightEmus * 1.0m) / widthEmus;
widthEmus = maxWidthEmus;
heightEmus = (long)(widthEmus * ratio);
}
// Define the reference of the image.
var element =
new Drawing(
new DW.Inline(
new DW.Extent() { Cx = 990000L, Cy = 792000L },
new DW.EffectExtent()
{
LeftEdge = 0L,
TopEdge = 0L,
RightEdge = 0L,
BottomEdge = 0L
},
new DW.DocProperties()
{
Id = (UInt32Value)1U,
Name = "Picture 1"
},
new DW.NonVisualGraphicFrameDrawingProperties(
new A.GraphicFrameLocks() { NoChangeAspect = true }),
new A.Graphic(
new A.GraphicData(
new PIC.Picture(
new PIC.NonVisualPictureProperties(
new PIC.NonVisualDrawingProperties()
{
Id = (UInt32Value)0U,
Name = "New Bitmap Image.jpg"
},
new PIC.NonVisualPictureDrawingProperties()),
new PIC.BlipFill(
new A.Blip(
new A.BlipExtensionList(
new A.BlipExtension()
{
Uri =
"{28A0092B-C50C-407E-A947-70E740481C1C}"
})
)
{
Embed = relationshipId,
CompressionState = A.BlipCompressionValues.Print
},
new A.Stretch(
new A.FillRectangle())),
new PIC.ShapeProperties(
new A.Transform2D(
new A.Offset() { X = 0L, Y = 0L },
new A.Extents() { Cx = widthEmus, Cy = heightEmus }),
new A.PresetGeometry(
new A.AdjustValueList()
) { Preset = A.ShapeTypeValues.Rectangle }))
) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
)
{
DistanceFromTop = (UInt32Value)0U,
DistanceFromBottom = (UInt32Value)0U,
DistanceFromLeft = (UInt32Value)0U,
DistanceFromRight = (UInt32Value)0U,
EditId = "50D07946"
});
// Append the reference to body, the element should be in a Run.
wordDoc.MainDocumentPart.Document.Body.AppendChild(new Paragraph(new Run(element)));
}
Ответ №1:
Размеры в EMU (английская метрическая единица — прочтите это для хорошего объяснения) задаются в экстентах (Cx и Cy). Чтобы вставить изображение в DocX, я обычно делаю это следующим образом:
- Получаем размеры и разрешение изображения
- Вычислите ширину изображения в EMU: wEmu = imgWidthPixels / imgHorizontalDpi * emuPerInch
- Вычислите высоту изображения в EMU: hEmu = imgHeightPixels / imgVerticalDpi * emuPerInch
- Вычислите максимальную ширину страницы в EMUs (я обнаружил, что если изображение слишком широкое, оно не будет отображаться)
-
Если ширина изображения в EMUs больше максимальной ширины страницы, я масштабирую ширину и высоту изображения так, чтобы ширина изображения была равна ширине страницы (когда я говорю «страница», я имею в виду «полезную» страницу, то есть минус поля):
var ratio = hEmu / wEmu;
wEmu = maxPageWidthEmu;
hEmu = wEmu * ratio;
-
Затем я использую ширину как значение Cx, а высоту как значение Cy, как в
DW.Extent
, так и вA.Extents
(изA.Transform2D
PIC.ShapeProperties
).
Обратите внимание, что значение emuPerInch равно 914400.
У меня это запущено (в службе), но у меня сейчас нет с собой кода.
Обновить
Это код, который я использую:
var img = new BitmapImage(new Uri(fileName, UriKind.RelativeOrAbsolute));
var widthPx = img.PixelWidth;
var heightPx = img.PixelHeight;
var horzRezDpi = img.DpiX;
var vertRezDpi = img.DpiY;
const int emusPerInch = 914400;
const int emusPerCm = 360000;
var widthEmus = (long)(widthPx / horzRezDpi * emusPerInch);
var heightEmus = (long)(heightPx / vertRezDpi * emusPerInch);
var maxWidthEmus = (long)(maxWidthCm * emusPerCm);
if (widthEmus > maxWidthEmus) {
var ratio = (heightEmus * 1.0m) / widthEmus;
widthEmus = maxWidthEmus;
heightEmus = (long)(widthEmus * ratio);
}
В моем случае размеры моей страницы указаны в сантиметрах, отсюда и emusPerCm, который вы видите выше.
ОБНОВЛЕНИЕ 2 (для ответа @andw)
Чтобы файл был заблокирован в течение минимально возможного времени, откройте его с помощью FileStream
и передайте результирующий поток в StreamSource
свойство BitmapImage
:
var img = new BitmapImage();
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
img.BeginInit();
img.StreamSource = fs;
img.EndInit();
}
// The file is now unlocked
var widthPx = img.PixelWidth;
...
Комментарии:
1. Является ли dpi изображения постоянным как для ширины, так и для высоты? как мне это вычислить?
2. Я рассматриваю их как непостоянные, но мне еще предстоит найти тот, где их нет. Если вы откроете изображение с помощью класса BitmapImage (System. Windows. Медиафайлы. Пространство имен Imaging), оно обладает следующими свойствами, которые вы можете использовать:
PixelWidth
,PixelHeight
,DpiX
,DpiY
.,,,,,,,,,,,.3. Я обновил свой пост новым кодом. Я думаю, что у меня правильный код, но размер изображения не меняется.
4. Вы не установили DW.Extent. Вот так: (…) новый DW.Extent() { Cx = widthEmus, Cy = heightEmus }, (…)
5. @Арджан «Магия». В нашем случае это доступная ширина между полями. Все наши документы, сгенерированные этим сервисом, имеют фиксированные левое и правое поля, которые, вычитаемые из ширины листа формата А4 (в портретном режиме), дают примерно 15 см (наш
maxWidthCm
). Вы устанавливаете то, что полезно для вашего случая.
Ответ №2:
Таким образом вы можете придать изображению его первоначальный размер:
Сначала вы получаете ширину и высоту файла:
int iWidth = 0;
int iHeight = 0;
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap("yourFilePath"))
{
iWidth = bmp.Width;
iHeight = bmp.Height;
}
Затем преобразуйте пиксели в EMU таким образом:
iWidth = (int)Math.Round((decimal)iWidth * 9525);
iHeight = (int)Math.Round((decimal)iHeight * 9525);
И, наконец, укажите значения при открытии вашего файла, я имею в виду в этой строке вашего кода:
new DW.Extent() { Cx = 990000L, Cy = 792000L },
замените Cx и Cy вашими вычисленными значениями, чтобы это выглядело следующим образом:
new DW.Extent() { Cx = iWidth, Cy = iHeight },
Обратите внимание, что в вашем коде есть две строки, где вы должны указать вычисленные значения.
Тогда у вас будет изображение в исходном размере, надеюсь, это кому-нибудь поможет.
Комментарии:
1. Обратите внимание, что в вашем коде есть две строки, где вы должны указать вычисленные значения… Это ключевая часть
2. Голосую за, потому что это решение простое, но работает.
Ответ №3:
Этот код работает для меня.
Источник: http://msdn.microsoft.com/en-us/library/office/bb497430 (v=office.15).aspx
public static void Do()
{
string filename = @"c:tempm.docx";
byte[] reportData = GetWordReport();
// File.WriteAllBytes(filename, reportData);
//MessageBox.Show("File " filename " created");
}
private static byte[] GetWordReport()
{
// using (MemoryStream stream = new MemoryStream())
// {
//var template = GetTemplateData();
//stream.Write(template, 0, template.Length);
using (WordprocessingDocument docx = WordprocessingDocument.Open(@"c:tempm.docx", true))
{
// Some changes on docx
docx.MainDocumentPart.Document = GenerateMainDocumentPart(6,4);
var imagePart = docx.MainDocumentPart.AddNewPart<ImagePart>("image/jpeg", "rIdImagePart1");
GenerateImagePart(imagePart);
}
// stream.Seek(0, SeekOrigin.Begin);
// return stream.ToArray();
// }
return null;
}
private static byte[] GetTemplateData()
{
using (MemoryStream targetStream = new MemoryStream())
using (BinaryReader sourceReader = new BinaryReader(File.Open(@"c:tempm_2.docx", FileMode.Open)))
{
byte[] buffer = new byte[4096];
int num = 0;
do
{
num = sourceReader.Read(buffer, 0, 4096);
if (num > 0)
targetStream.Write(buffer, 0, num);
}
while (num > 0);
targetStream.Seek(0, SeekOrigin.Begin);
return targetStream.ToArray();
}
}
private static void GenerateImagePart(OpenXmlPart part)
{
using (Stream imageStream = File.Open(@"c:tempimage002.jpg", FileMode.Open))
{
part.FeedData(imageStream);
}
}
private static Document GenerateMainDocumentPart(int cx,int cy)
{
long LCX = cx*261257L;
long LCY = cy*261257L;
var element =
new Document(
new Body(
new Paragraph(
new Run(
new RunProperties(
new NoProof()),
new Drawing(
new wp.Inline(
new wp.Extent() { Cx = LCX, Cy = LCY },
new wp.EffectExtent() { LeftEdge = 0L, TopEdge = 19050L, RightEdge = 0L, BottomEdge = 0L },
new wp.DocProperties() { Id = (UInt32Value)1U, Name = "Picture 0", Description = "Forest Flowers.jpg" },
new wp.NonVisualGraphicFrameDrawingProperties(
new a.GraphicFrameLocks() { NoChangeAspect = true }),
new a.Graphic(
new a.GraphicData(
new pic.Picture(
new pic.NonVisualPictureProperties(
new pic.NonVisualDrawingProperties() { Id = (UInt32Value)0U, Name = "Forest Flowers.jpg" },
new pic.NonVisualPictureDrawingProperties()),
new pic.BlipFill(
new a.Blip() { Embed = "rIdImagePart1", CompressionState = a.BlipCompressionValues.Print },
new a.Stretch(
new a.FillRectangle())),
new pic.ShapeProperties(
new a.Transform2D(
new a.Offset() { X = 0L, Y = 0L },
new a.Extents() { Cx = LCX, Cy = LCY }),
new a.PresetGeometry(
new a.AdjustValueList()
) { Preset = a.ShapeTypeValues.Rectangle }))
) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" })
) { DistanceFromTop = (UInt32Value)0U, DistanceFromBottom = (UInt32Value)0U, DistanceFromLeft = (UInt32Value)0U, DistanceFromRight = (UInt32Value)0U }))
) { RsidParagraphAddition = "00A2180E", RsidRunAdditionDefault = "00EC4DA7" },
new SectionProperties(
new PageSize() { Width = (UInt32Value)11906U, Height = (UInt32Value)16838U },
new PageMargin() { Top = 1440, Right = (UInt32Value)1800U, Bottom = 1440, Left = (UInt32Value)1800U, Header = (UInt32Value)851U, Footer = (UInt32Value)992U, Gutter = (UInt32Value)0U },
new Columns() { Space = ((UInt32Value)425U).ToString() },
new DocGrid() { Type = DocGridValues.Lines, LinePitch = 312 }
) { RsidR = "00A2180E", RsidSect = "00A2180E" }));
return element;
}
Комментарии:
1. Не хотите добавить источник, где вы это найдете? Поскольку это не ваша собственная «находка».
Ответ №4:
Поскольку это один из первых хитов при поиске изображений и OpenXML, и мир немного изменился, я хотел бы поделиться решением @ssarabando для вычисления EMU, адаптированным для .net core, где System.Drawing
недоступно, с использованием ImageSharp (все еще бета-версия по состоянию на ноябрь 2018):
const int emusPerInch = 914400;
const int emusPerCm = 360000;
long widthEmus;
long heightEmus;
Image<Rgba32> img = Image.Load(sourceStream, new PngDecoder());
switch (img.MetaData.ResolutionUnits)
{
case PixelResolutionUnit.PixelsPerCentimeter :
widthEmus = (long)(img.Width / img.MetaData.HorizontalResolution * emusPerCm);
heightEmus = (long)(img.Height / img.MetaData.VerticalResolution * emusPerCm);
break;
case PixelResolutionUnit.PixelsPerInch:
widthEmus = (long)(img.Width / img.MetaData.HorizontalResolution * emusPerInch);
heightEmus = (long)(img.Height / img.MetaData.VerticalResolution * emusPerInch);
break;
case PixelResolutionUnit.PixelsPerMeter:
widthEmus = (long)(img.Width / img.MetaData.HorizontalResolution * emusPerCm * 100);
heightEmus = (long)(img.Height / img.MetaData.VerticalResolution * emusPerCm * 100);
break;
default:
widthEmus = 2000000;
heightEmus = 2000000;
break;
}
Надеюсь, это сэкономит чье-то время.
Комментарии:
1. Система. Рисование доступно в .Net Core. Чего нет, так это BitmapImage, но вы можете использовать Bitmap. Смотрите ответ @ JCO9.
Ответ №5:
Это то, что работает для меня в Net Core и реализовано здесь.
long iWidth = 0;
long iHeight = 0;
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(fullPathToImageFile))
{
iWidth = bmp.Width;
iHeight = bmp.Height;
}
iWidth = (long)Math.Round((decimal)iWidth * 9525);
iHeight = (long)Math.Round((decimal)iHeight * 9525);
double maxWidthCm = 17.4; // Our current margins gives us 17.4cm of space
const int emusPerCm = 360000;
long maxWidthEmus = (long)(maxWidthCm * emusPerCm);
if (iWidth > maxWidthEmus) {
var ratio = (iHeight * 1.0m) / iWidth;
iWidth = maxWidthEmus;
iHeight = (long)(iWidth * ratio);
}