diff --git a/docx/image/__init__.py b/docx/image/__init__.py index 8ab3ada68..c241024fa 100644 --- a/docx/image/__init__.py +++ b/docx/image/__init__.py @@ -14,6 +14,7 @@ from docx.image.jpeg import Exif, Jfif from docx.image.png import Png from docx.image.tiff import Tiff +from docx.image.emf import Emf SIGNATURES = ( @@ -26,4 +27,5 @@ (Tiff, 0, b'MM\x00*'), # big-endian (Motorola) TIFF (Tiff, 0, b'II*\x00'), # little-endian (Intel) TIFF (Bmp, 0, b'BM'), + (Emf, 40, b' EMF') ) diff --git a/docx/image/constants.py b/docx/image/constants.py index 90b469705..93fbc0f19 100644 --- a/docx/image/constants.py +++ b/docx/image/constants.py @@ -102,6 +102,7 @@ class MIME_TYPE(object): JPEG = 'image/jpeg' PNG = 'image/png' TIFF = 'image/tiff' + EMF = 'image/emf' class PNG_CHUNK_TYPE(object): diff --git a/docx/image/emf.py b/docx/image/emf.py new file mode 100644 index 000000000..f0a4fe705 --- /dev/null +++ b/docx/image/emf.py @@ -0,0 +1,70 @@ +# encoding: utf-8 + +from __future__ import absolute_import, division, print_function + +from .constants import MIME_TYPE +from .exceptions import InvalidImageStreamError +from .helpers import BIG_ENDIAN, StreamReader +from .image import BaseImageHeader +import struct + +class Emf(BaseImageHeader): + """ + Image header parser for PNG images + """ + @property + def content_type(self): + """ + MIME content type for this image, unconditionally `image/png` for + PNG images. + """ + return MIME_TYPE.EMF + + @property + def default_ext(self): + """ + Default filename extension, always 'png' for PNG images. + """ + return 'emf' + + @classmethod + def from_stream(cls, stream,filename=None): + """ + Return a |Emf| instance having header properties parsed from image in + *stream*. + """ + + """ + @0 DWORD iType; // fixed + @4 DWORD nSize; // var + @8 RECTL rclBounds; + @24 RECTL rclFrame; // .01 millimeter units L T R B + @40 DWORD dSignature; // ENHMETA_SIGNATURE = 0x464D4520 + DWORD nVersion; + DWORD nBytes; + DWORD nRecords; + WORD nHandles; + WORD sReserved; + DWORD nDescription; + DWORD offDescription; + DWORD nPalEntries; + SIZEL szlDevice; + SIZEL szlMillimeters; + """ + stream.seek(0) + x = stream.read(40) + stream.seek(0) + iType,nSize = struct.unpack("ii",x[0:8]) + rclBounds = struct.unpack("iiii",x[8:24]) + rclFrame = struct.unpack("iiii",x[24:40]) + + dpi = 300 + horz_dpi = dpi + vert_dpi = dpi + mmwidth = (rclFrame[2]-rclFrame[0])/100.0 + mmheight = (rclFrame[3]-rclFrame[1])/100.0 + px_width = int(mmwidth*dpi*0.03937008) + px_height = int(mmheight*dpi*0.03937008) + + #1 dot/inch = 0.03937008 pixel/millimeter + return cls(px_width,px_height,horz_dpi,vert_dpi) diff --git a/docx/image/image.py b/docx/image/image.py index ba2158e72..3df31672f 100644 --- a/docx/image/image.py +++ b/docx/image/image.py @@ -188,7 +188,7 @@ def _ImageHeaderFactory(stream): def read_32(stream): stream.seek(0) - return stream.read(32) + return stream.read(64) header = read_32(stream) for cls, offset, signature_bytes in SIGNATURES: diff --git a/samples/testaddemf.emf b/samples/testaddemf.emf new file mode 100755 index 000000000..2d0958cc9 Binary files /dev/null and b/samples/testaddemf.emf differ diff --git a/samples/testaddemf.py b/samples/testaddemf.py new file mode 100644 index 000000000..c455f1816 --- /dev/null +++ b/samples/testaddemf.py @@ -0,0 +1,32 @@ +import docx +from docx import Document +from docx.oxml.ns import nsdecls +from docx.oxml import parse_xml +from docx.enum.text import WD_ALIGN_PARAGRAPH +from docx.shared import Mm + + +def docx_addpicture(document, path, width_mm, caption): + paragraph = document.add_paragraph() + paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER + run = paragraph.add_run() + run.add_picture(path, width=Mm(width_mm)) + # DOCX missing bookmarks + # DOCX numbering goes next paragraph (WHY?) + # See Issue: https://github.com/python-openxml/python-docx/issues/138 + if caption != "": + paragraph = document.add_paragraph(style="Caption") + p = paragraph._p # this is the actual lxml element for a paragraph + run = paragraph.add_run("Figure ") + fld_xml = ('' % + nsdecls('w')) + fldSimple = parse_xml(fld_xml) # this goes AFTER the field (!) + #p.addnext(fldSimple) + paragraph.add_run(" " + caption) + + +document = Document() + +docx_addpicture(document,"testaddemf.emf", width_mm=100,caption="Groups Implementation") + +document.save("testaddemf.docx") \ No newline at end of file