Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Image scale to fit #35

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion DocX/DocX.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protocol DocX{
options:DocXOptions) throws ->String
func writeDocX(to url:URL)throws
func writeDocX(to url:URL, options:DocXOptions) throws
func prepareLinks(linkXML:AEXMLDocument, mediaURL:URL)->[DocumentRelationship]
func prepareLinks(linkXML:AEXMLDocument, mediaURL:URL, options:DocXOptions)->[DocumentRelationship]
}

public let docXUTIType="org.openxmlformats.wordprocessingml.document"
Expand Down
8 changes: 4 additions & 4 deletions DocX/DocXWriting.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ extension DocX where Self : NSAttributedString{
return lastIdIDX
}

func prepareLinks(linkXML: AEXMLDocument, mediaURL:URL) -> [DocumentRelationship] {
func prepareLinks(linkXML: AEXMLDocument, mediaURL:URL, options:DocXOptions) -> [DocumentRelationship] {
var linkURLS=[URL]()

let imageRelationships = prepareImages(linkXML: linkXML, mediaURL:mediaURL)
let imageRelationships = prepareImages(linkXML: linkXML, mediaURL:mediaURL, options: options)

self.enumerateAttribute(.link, in: NSRange(location: 0, length: self.length), options: [.longestEffectiveRangeNotRequired], using: {attribute, _, stop in
if let link=attribute as? URL{
Expand All @@ -131,7 +131,7 @@ extension DocX where Self : NSAttributedString{
return linkRelationShips + imageRelationships
}

func prepareImages(linkXML: AEXMLDocument, mediaURL:URL) -> [DocumentRelationship]{
func prepareImages(linkXML: AEXMLDocument, mediaURL:URL, options:DocXOptions) -> [DocumentRelationship]{
var attachements=[NSTextAttachment]()
self.enumerateAttribute(.attachment, in: NSRange(location: 0, length: self.length), options: [.longestEffectiveRangeNotRequired], using: {attribute, _, stop in
if let link=attribute as? NSTextAttachment{
Expand Down Expand Up @@ -181,7 +181,7 @@ extension DocX where Self : NSAttributedString{
// Return the image relationship
return ImageRelationship(relationshipID: newID,
linkURL: destURL,
attachement: attachement)
attachement: attachement, pageDefinition: options.pageDefinition)
} else {
// Something went wrong
return nil
Expand Down
10 changes: 8 additions & 2 deletions DocX/ImageRelationship.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ struct ImageRelationship: DocumentRelationship{
let relationshipID:String
let linkURL:URL
let attachement:NSTextAttachment
let pageDefinition:PageDefinition?


}

extension ImageRelationship{
Expand Down Expand Up @@ -97,7 +100,9 @@ extension ImageRelationship{
let frameProperties=AEXMLElement(name: "wp:cNvGraphicFramePr")
frameProperties.addChild(AEXMLElement(name: "a:graphicFrameLocks", value: nil, attributes: ["xmlns:a":"http://schemas.openxmlformats.org/drawingml/2006/main", "noChangeAspect":"1"]))

inline.addChildren(attachement.extentAttributes + [docPr, frameProperties, graphic])
let extentAttributes=attachement.extentAttributes(pageSize: pageDefinition)

inline.addChildren(extentAttributes + [docPr, frameProperties, graphic])

let graphicData=AEXMLElement(name: "a:graphicData", value: nil, attributes: ["uri":"http://schemas.openxmlformats.org/drawingml/2006/picture"])

Expand Down Expand Up @@ -126,7 +131,8 @@ extension ImageRelationship{
pic.addChild(shapeProperties)
let xFrame=AEXMLElement(name: "a:xfrm")
xFrame.addChild(AEXMLElement(name: "a:off", value: nil, attributes: ["x":"0","y":"0"]))
let extent=AEXMLElement(name: "a:ext", value: nil, attributes: self.attachement.extentInEMU.extentAttributes)
let extentInEmu=attachement.extentInEMU(size: attachement.extent(for: pageDefinition))
let extent=AEXMLElement(name: "a:ext", value: nil, attributes: extentInEmu.extentAttributes)
xFrame.addChild(extent)
shapeProperties.addChild(xFrame)

Expand Down
2 changes: 1 addition & 1 deletion DocX/NSAttributedString+DocX-macOS.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ extension NSAttributedString{
options.escape = true

let linkDocument=try AEXMLDocument(xml: linkData, options: options)
let linkRelations=self.prepareLinks(linkXML: linkDocument, mediaURL: mediaURL)
let linkRelations=self.prepareLinks(linkXML: linkDocument, mediaURL: mediaURL, options: DocXOptions())
let updatedLinks=linkDocument.xmlCompact
try updatedLinks.write(to: linkURL, atomically: true, encoding: .utf8)

Expand Down
2 changes: 1 addition & 1 deletion DocX/NSAttributedString+Writing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ extension NSAttributedString{
linkDocument.root.addChild(name: "Relationship", value: nil, attributes: attrs)
}

let linkRelations=self.prepareLinks(linkXML: linkDocument, mediaURL: mediaURL)
let linkRelations=self.prepareLinks(linkXML: linkDocument, mediaURL: mediaURL, options: options)
let updatedLinks=linkDocument.xmlCompact
try updatedLinks.write(to: linkURL, atomically: true, encoding: .utf8)

Expand Down
52 changes: 42 additions & 10 deletions DocX/NSTextAttachement+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ extension NSTextAttachment{
let width:Int
let height:Int

init(cgSize:CGSize) {
self.height=Int(cgSize.height)
self.width=Int(cgSize.width)
}


var extentAttribute:AEXMLElement{
return AEXMLElement(name: "wp:extent", value: nil, attributes: self.extentAttributes)
}
Expand Down Expand Up @@ -68,7 +74,13 @@ extension NSTextAttachment{
}
}

var extentInEMU:Size{
/// Autosizing behavior of images.
///
/// The default behaviour is:
/// - if an explicit `bounds` has been supplied for the `NSTextAttachement`, use this size (in points)
/// - if no `bounds` has been suplied (`bounds == CGRect.zero`), use the size of the supplied image (in points).
/// - if an optional `PageDefinition` is supplied and no explicit size for the `NSTextAttachement` has been set, scale the image to fit the page if it is larger than the printable area, otherwise do nothing.
func extent(for pageSize:PageDefinition?) -> CGSize{
let size:CGSize
if self.bounds != .zero{
size=self.bounds.size
Expand All @@ -77,19 +89,39 @@ extension NSTextAttachment{
size=self.dataImageSize
}

let width=size.width
let height=size.height

let width:CGFloat
let height:CGFloat


// we have a page size defined and the image is larger (in one dimension) than the page. we shrink the image to fit the printable area of the page.
// If there is a user-defined size, we accept this even if it is too large.
if let bounds=pageSize?.printableSize(unit: .points),
size == .zero,
(bounds.height < size.height || bounds.width < size.width) {
let ratio=min(bounds.height / size.height, bounds.width / size.width)
let scaledSize=size.applying(.init(scaleX: ratio, y: ratio))
width=scaledSize.width
height=scaledSize.height
}
else{
width=size.width
height=size.height
}

return CGSize(width: width, height: height)
}


func extentInEMU(size:CGSize) -> Size{
let emuPerInch=CGFloat(914400)
let dpi=CGFloat(72)
let emuWidth=width/dpi*emuPerInch
let emuHeight=height/dpi*emuPerInch
return Size(width: Int(emuWidth), height: Int(emuHeight))

let emuSize=size.applying(.init(scaleX: emuPerInch / dpi, y: emuPerInch / dpi))
return Size(cgSize: emuSize)
}

var extentAttributes:[AEXMLElement]{
let size=self.extentInEMU

func extentAttributes(pageSize:PageDefinition?) -> [AEXMLElement]{
let size=extentInEMU(size: extent(for: pageSize))
let extent=size.extentAttribute
let effectiveExtent=AEXMLElement(name: "wp:effectExtent", value: nil, attributes: ["l":"0", "t":"0","r":"0","b":"0"])
return [extent,effectiveExtent]
Expand Down
25 changes: 25 additions & 0 deletions DocXTests/DocXTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,31 @@ let string = """

}

func testScaleImageToSize() throws{
let loremIpsum = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
"""

let imageURL=try XCTUnwrap(bundle.url(forResource: "lenna", withExtension: "png"), "ImageURL not found")
let imageData=try XCTUnwrap(Data(contentsOf: imageURL), "Image not found")
let attachement=NSTextAttachment(data: imageData, ofType: kUTTypePNG as String)

let text=NSMutableAttributedString()
text.append(NSAttributedString(string: loremIpsum, attributes: [.foregroundColor: NSColor.red]))
text.append(NSAttributedString(string: "\r"))
text.append(NSAttributedString(attachment: attachement))
text.append(NSAttributedString(string: loremIpsum, attributes: [.foregroundColor: NSColor.black, .font: NSFont(name: "Helvetica", size: 19)!]))

let defs = [PageDefinition(pageSize: .A4)]

for def in defs{
var options=DocXOptions()
options.pageDefinition=def
try writeAndValidateDocX(attributedString: text, options: options)
}


}

// MARK: Performance Tests

Expand Down