diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/DocX.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/DocX.xcscheme index c3cd927..9fba008 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/DocX.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/DocX.xcscheme @@ -48,6 +48,34 @@ ReferencedContainer = "container:"> + + + + + + + + DocX-Examples-iOS.xcscheme_^#shared#^_ orderHint - 1 + 0 DocX-Examples-macOS.xcscheme_^#shared#^_ diff --git a/DocX-iOS-Tests/DocX_iOS_Tests.swift b/DocX-iOS-Tests/DocX_iOS_Tests.swift index 4f8ef0d..e4bae8b 100644 --- a/DocX-iOS-Tests/DocX_iOS_Tests.swift +++ b/DocX-iOS-Tests/DocX_iOS_Tests.swift @@ -16,15 +16,13 @@ import UniformTypeIdentifiers @testable import DocX -@available(iOS 10.0, *) -class DocX_iOS_Tests: XCTestCase { - #if SWIFT_PACKAGE - let bundle=Bundle.module -#else - let bundle=Bundle(for: DocX_iOS_Tests.self) +import DocXTestsCommon #endif +@available(iOS 10.0, *) +class DocX_iOS_Tests: XCTestCase, DocXTesting { + var tempURL:URL=URL(fileURLWithPath: "") override func setUp() { @@ -66,54 +64,31 @@ class DocX_iOS_Tests: XCTestCase { - func testWriteDocX(attributedString:NSAttributedString){ - - do{ - let url=self.tempURL.appendingPathComponent(UUID().uuidString + "_myDocument_\(attributedString.string.prefix(10))").appendingPathExtension("docx") - try attributedString.writeDocX(to: url) -// var readAttributes:NSDictionary?=nil -// let docXString=try NSAttributedString(url: url, options: [:], documentAttributes: &readAttributes) -// guard let attributes=readAttributes as? [String:Any] else{ -// XCTFail() -// return -// } -// XCTAssertEqual(attributes[NSAttributedString.DocumentAttributeKey.documentType.rawValue] as! String, NSAttributedString.DocumentType.officeOpenXML.rawValue) -// let string=docXString.string -// print(string) -// XCTAssertEqual(docXString.string, string) - - } - catch let error{ - XCTFail(error.localizedDescription) - } - } - - - func testBlank(){ + func testBlank() throws{ let string="" let attributedString=NSAttributedString(string: string) - testWriteDocX(attributedString: attributedString) + try writeAndValidateDocX(attributedString: attributedString) } - func test山田Plain() { + func test山田Plain() throws { let string="山田" - testWriteDocX(attributedString: NSAttributedString(string: string)) + try writeAndValidateDocX(attributedString: NSAttributedString(string: string)) } - func test山田Attributed() { + func test山田Attributed() throws { let string="山田" let attributed=NSAttributedString(string: string, attributes: [.font:UIFont.systemFont(ofSize: UIFont.systemFontSize)]) - testWriteDocX(attributedString: attributed) + try writeAndValidateDocX(attributedString: attributed) } - func test山田FuriganaAttributed() { + func test山田FuriganaAttributed() throws{ let string="山田" let furigana="やまだ" let ruby=CTRubyAnnotationCreateWithAttributes(.auto, .auto, .before, furigana as CFString, [kCTRubyAnnotationSizeFactorAttributeName:0.5] as CFDictionary) let rubyKey=NSAttributedString.Key(kCTRubyAnnotationAttributeName as String) let attributed=NSAttributedString(string: string, attributes: [.font:UIFont.systemFont(ofSize: UIFont.systemFontSize), rubyKey:ruby]) - testWriteDocX(attributedString: attributed) + try writeAndValidateDocX(attributedString: attributed) } @@ -131,37 +106,37 @@ class DocX_iOS_Tests: XCTestCase { return attributedString } - func test山田電気FuriganaAttributed() { - testWriteDocX(attributedString: yamadaDenkiString) + func test山田電気FuriganaAttributed() throws { + try writeAndValidateDocX(attributedString: yamadaDenkiString) + } - func test山田電気FuriganaAttributed_ParagraphStyle() { + func test山田電気FuriganaAttributed_ParagraphStyle() throws { let attributed=yamadaDenkiString let style=NSParagraphStyle.default attributed.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: attributed.length)) - testWriteDocX(attributedString: attributed) - + try writeAndValidateDocX(attributedString: attributed) } - func test山田電気FuriganaAttributed_ParagraphStyle_vertical() { + func test山田電気FuriganaAttributed_ParagraphStyle_vertical() throws { let attributed=yamadaDenkiString let style=NSParagraphStyle.default attributed.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: attributed.length)) attributed.addAttribute(.verticalForms, value: true, range:NSRange(location: 0, length: attributed.length)) - testWriteDocX(attributedString: attributed) + try writeAndValidateDocX(attributedString: attributed) } - func test山田電気FuriganaAttributed_ParagraphStyle_italic() { + func test山田電気FuriganaAttributed_ParagraphStyle_italic() throws { let attributed=yamadaDenkiString let style=NSParagraphStyle.default attributed.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: attributed.length)) let boldFont=UIFont.italicSystemFont(ofSize: UIFont.systemFontSize) attributed.addAttribute(.font, value: boldFont, range: NSRange(location: 0, length: 2)) - testWriteDocX(attributedString: attributed) - + try writeAndValidateDocX(attributedString: attributed) + } - func testLink(){ + func testLink() throws{ let string="楽天 https://www.rakuten-sec.co.jp/" let attributed=NSMutableAttributedString(string: string) attributed.addAttributes([.font:NSFont.systemFont(ofSize: NSFont.systemFontSize)], range: NSRange(location: 0, length: attributed.length)) @@ -169,34 +144,29 @@ class DocX_iOS_Tests: XCTestCase { let furiganaAnnotation=CTRubyAnnotationCreateWithAttributes(.auto, .auto, .before, furigana as CFString, [kCTRubyAnnotationSizeFactorAttributeName:0.5] as CFDictionary) attributed.addAttribute(.ruby, value: furiganaAnnotation, range: NSRange(location: 0, length: 2)) attributed.addAttribute(.link, value: URL(string: "https://www.rakuten-sec.co.jp/")!, range: NSRange(location: 3, length: 30)) - testWriteDocX(attributedString: attributed) - + try writeAndValidateDocX(attributedString: attributed) } - func test山田電気FuriganaAttributed_ParagraphStyle_underline() { + func test山田電気FuriganaAttributed_ParagraphStyle_underline() throws { let attributed=yamadaDenkiString // let style=NSParagraphStyle.default // attributed.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: attributed.length)) let underlineStyle:NSUnderlineStyle = [.single,.byWord] attributed.addAttribute(.underlineStyle, value: underlineStyle.rawValue, range:NSRange(location: 0, length: attributed.length)) - testWriteDocX(attributedString: attributed) - - + try writeAndValidateDocX(attributedString: attributed) } - func test山田電気FuriganaAttributed_ParagraphStyle_strikethrough() { + func test山田電気FuriganaAttributed_ParagraphStyle_strikethrough() throws { let attributed=yamadaDenkiString // let style=NSParagraphStyle.default // attributed.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: attributed.length)) let underlineStyle:NSUnderlineStyle = [.single] attributed.addAttribute(.strikethroughStyle, value: underlineStyle.rawValue, range:NSRange(location: 0, length: attributed.length)) - testWriteDocX(attributedString: attributed) - - sleep(1) + try writeAndValidateDocX(attributedString: attributed) } - func test山田電気FuriganaAttributed_ParagraphStyle_backgroundColor() { + func test山田電気FuriganaAttributed_ParagraphStyle_backgroundColor() throws { let attributed=yamadaDenkiString let style=NSMutableParagraphStyle() style.setParagraphStyle(NSParagraphStyle.default) @@ -204,12 +174,11 @@ class DocX_iOS_Tests: XCTestCase { attributed.addAttribute(.paragraphStyle, value: style, range: NSRange(location: 0, length: attributed.length)) attributed.addAttribute(.backgroundColor, value: NSColor.blue, range:NSRange(location: 0, length: attributed.length)) - testWriteDocX(attributedString: attributed) - - sleep(1) + try writeAndValidateDocX(attributedString: attributed) + } - func testMultipage(){ + func testMultipage() throws{ let longString = """ 1. 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. @@ -221,8 +190,8 @@ class DocX_iOS_Tests: XCTestCase { 5. 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 attributed=NSAttributedString(string: longString, attributes: [.font:NSFont.systemFont(ofSize: 20)]) - testWriteDocX(attributedString: attributed) - + try writeAndValidateDocX(attributedString: attributed) + } @@ -231,11 +200,8 @@ class DocX_iOS_Tests: XCTestCase { let longString = """ 1. 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. """ -#if SWIFT_PACKAGE - let imageURL=try XCTUnwrap(Bundle.module.url(forResource: "Picture1", withExtension: "png")) -#else - let imageURL=try XCTUnwrap(Bundle(for: DocX_iOS_Tests.self).url(forResource: "Picture1", withExtension: "png")) -#endif + + let imageURL=try XCTUnwrap(bundle.url(forResource: "Picture1", withExtension: "png")) let imageData=try XCTUnwrap(Data(contentsOf: imageURL), "Image not found") let attachement=NSTextAttachment(data: imageData, ofType: kUTTypePNG as String) let attributed=NSAttributedString(string: longString, attributes: [.foregroundColor: NSColor.green]) @@ -243,7 +209,7 @@ class DocX_iOS_Tests: XCTestCase { let result=NSMutableAttributedString() result.append(attributed) result.append(imageString) - testWriteDocX(attributedString: result) + try writeAndValidateDocX(attributedString: result) } @@ -254,11 +220,10 @@ class DocX_iOS_Tests: XCTestCase { att.strokeWidth = -2 att.font = UIFont(name: "Helvetica", size: 12) att.foregroundColor = .gray - let title=String(att.characters.prefix(10)) - let url=self.tempURL.appendingPathComponent(UUID().uuidString + "_myDocument_\(title)").appendingPathExtension("docx") - print(url.absoluteString) + do{ - try att.writeDocX(to: url) + let attributed = NSAttributedString(att) + try writeAndValidateDocX(attributedString: attributed) } catch let error{ XCTFail(error.localizedDescription) @@ -310,8 +275,7 @@ And this is a [link](http://www.example.com). let font=NSFont(name: "Courier", size: 15)! string.append(NSAttributedString(string: "E=m•c", attributes: [.font:font])) string.append(NSAttributedString(string: "2", attributes: [.font:font, .baselineOffset:1])) - let temp=self.tempURL.appendingPathComponent(UUID().uuidString + "_myDocument_\("Subscript")").appendingPathExtension("docx") - try string.writeDocX(to: temp) + try writeAndValidateDocX(attributedString: string) } @@ -345,10 +309,8 @@ And this is a [link](http://www.example.com). att.append(newLine) } - let temp=self.tempURL.appendingPathComponent(UUID().uuidString + "_myDocument_\("Attachements")").appendingPathExtension("docx") - try att.writeDocX(to: temp) + try writeAndValidateDocX(attributedString: att) - } } diff --git a/DocX-iOS-Tests/ImageGeneration.swift b/DocX-iOS-Tests/ImageGeneration.swift deleted file mode 100644 index 73c1a7a..0000000 --- a/DocX-iOS-Tests/ImageGeneration.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// ImageGeneration.swift -// -// -// Created by Morten Bertz on 2023/03/05. -// - -import Foundation -import CoreGraphics - -#if(canImport(UniformTypeIdentifiers)) -import UniformTypeIdentifiers -#endif - -import ImageIO - -#if canImport(UIKit) -import UIKit -#elseif canImport(AppKit) -import AppKit -fileprivate typealias UIFont = NSFont -fileprivate typealias UIColor = NSColor -#endif - -@available(iOS 16.0, macOS 13.0, *) -class ImageGenerator{ - - enum ImageGenerationError: Error{ - case unsupportedFileType - case imageWritingError - } - - let size:CGSize - - init(size:CGSize){ - self.size=size - } - - func generateImage(type:UTType) throws -> URL{ - let outURL=FileManager.default.temporaryDirectory.appending(path: UUID().uuidString).appendingPathExtension(for: type) - switch type{ - case .pdf: - var rect=CGRect(origin: .zero, size: size) - guard let ctx=CGContext(outURL as CFURL, mediaBox: &rect, nil) else{ - throw ImageGenerationError.imageWritingError - } - ctx.beginPDFPage(nil) - draw(in: ctx, type: type) - ctx.endPDFPage() - ctx.closePDF() - return outURL - - case .png, .jpeg, .tiff: - let info=CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue) - guard let ctx=CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: info.rawValue) - else{ - throw ImageGenerationError.imageWritingError - } - let rect=CGRect(origin: .zero, size: size) - ctx.clear(rect) - draw(in: ctx, type: type) - guard let image=ctx.makeImage(), - let dest=CGImageDestinationCreateWithURL(outURL as CFURL, type.identifier as CFString, 1, nil) - else{ - throw ImageGenerationError.imageWritingError - } - CGImageDestinationAddImage(dest, image, nil) - let retVal=CGImageDestinationFinalize(dest) - guard retVal == true else{ - throw ImageGenerationError.imageWritingError - } - - return outURL - default: - throw ImageGenerationError.unsupportedFileType - } - } - - - private func draw(in ctx:CGContext, type:UTType){ - let rect=CGRect(origin: .zero, size: size) - - ctx.setFillColor(red: 1, green: 0, blue: 0, alpha: 0.9) - ctx.fillEllipse(in: rect) - ctx.textMatrix = .identity - - let text=NSAttributedString(string: "DocX \(type.localizedDescription ?? "")", attributes: [.font: UIFont(name: "Helvetica", size: size.height / 10.0) as Any,.foregroundColor: UIColor.black]) - let textRect=text.boundingRect(with: size, context: nil) - let origin=CGPoint(x: (rect.width - textRect.width)/2, y: (rect.height - textRect.height)/2) - let line=CTLineCreateWithAttributedString(text) - ctx.saveGState() - ctx.translateBy(x: origin.x, y: origin.y) - CTLineDraw(line, ctx) - ctx.restoreGState() - } -} diff --git a/DocX.xcodeproj/project.pbxproj b/DocX.xcodeproj/project.pbxproj index 69560aa..8b788aa 100644 --- a/DocX.xcodeproj/project.pbxproj +++ b/DocX.xcodeproj/project.pbxproj @@ -7,22 +7,26 @@ objects = { /* Begin PBXBuildFile section */ - 5A2E47BD29B4535200DFAC1F /* DocXStyleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2E47BC29B4535200DFAC1F /* DocXStyleConfiguration.swift */; }; - 5A2E47BE29B4535200DFAC1F /* DocXStyleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2E47BC29B4535200DFAC1F /* DocXStyleConfiguration.swift */; }; - 5A2E47C029B4536300DFAC1F /* ImageGeneration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2E47BF29B4536300DFAC1F /* ImageGeneration.swift */; }; - 5A2E47C129B4537D00DFAC1F /* ImageGeneration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2E47BF29B4536300DFAC1F /* ImageGeneration.swift */; }; - 5A2E47C329B453AB00DFAC1F /* styles.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5A2E47C229B453AB00DFAC1F /* styles.xml */; }; 5A4FC7E927FEF5630063F796 /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A4FC7E827FEF5630063F796 /* AttributedString.swift */; }; 5A4FC7EA27FEF5630063F796 /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A4FC7E827FEF5630063F796 /* AttributedString.swift */; }; - 5A4FC7F827FFB4630063F796 /* lenna.md in Resources */ = {isa = PBXBuildFile; fileRef = 5A4FC7F727FFB4630063F796 /* lenna.md */; }; - 5A4FC7FA27FFB46C0063F796 /* lenna.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A4FC7F927FFB46C0063F796 /* lenna.png */; }; - 5A4FC7FC27FFB6240063F796 /* Picture1.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A4FC7FB27FFB6240063F796 /* Picture1.png */; }; - 5A4FC7FD27FFB6430063F796 /* lenna.md in Resources */ = {isa = PBXBuildFile; fileRef = 5A4FC7F727FFB4630063F796 /* lenna.md */; }; - 5A4FC7FE27FFB6460063F796 /* lenna.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A4FC7F927FFB46C0063F796 /* lenna.png */; }; 5A6B3DBA26D86CB1009F6859 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 5A6B3DB926D86CB1009F6859 /* ZIPFoundation */; }; 5A6B3DBD26D86CC0009F6859 /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 5A6B3DBC26D86CC0009F6859 /* ZIPFoundation */; }; 5A6B3DBF26D86D33009F6859 /* DocXOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A6B3DBE26D86D33009F6859 /* DocXOptions.swift */; }; 5A6B3DC026D86D33009F6859 /* DocXOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A6B3DBE26D86D33009F6859 /* DocXOptions.swift */; }; + 5A80F53F29B46D3E00992E96 /* ImageGeneration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A80F53829B46D3E00992E96 /* ImageGeneration.swift */; }; + 5A80F54129B46D3E00992E96 /* styles.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53929B46D3E00992E96 /* styles.xml */; }; + 5A80F54229B46D3E00992E96 /* styles.xml in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53929B46D3E00992E96 /* styles.xml */; }; + 5A80F54329B46D3E00992E96 /* Picture1.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53A29B46D3E00992E96 /* Picture1.png */; }; + 5A80F54429B46D3E00992E96 /* Picture1.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53A29B46D3E00992E96 /* Picture1.png */; }; + 5A80F54529B46D3E00992E96 /* lenna.md in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53B29B46D3E00992E96 /* lenna.md */; }; + 5A80F54629B46D3E00992E96 /* lenna.md in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53B29B46D3E00992E96 /* lenna.md */; }; + 5A80F54729B46D3E00992E96 /* blank.docx in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53C29B46D3E00992E96 /* blank.docx */; }; + 5A80F54829B46D3E00992E96 /* blank.docx in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53C29B46D3E00992E96 /* blank.docx */; }; + 5A80F54929B46D3E00992E96 /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A80F53D29B46D3E00992E96 /* Common.swift */; }; + 5A80F54B29B46D3E00992E96 /* lenna.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53E29B46D3E00992E96 /* lenna.png */; }; + 5A80F54C29B46D3E00992E96 /* lenna.png in Resources */ = {isa = PBXBuildFile; fileRef = 5A80F53E29B46D3E00992E96 /* lenna.png */; }; + 5A80F54D29B46D4C00992E96 /* ImageGeneration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A80F53829B46D3E00992E96 /* ImageGeneration.swift */; }; + 5A80F54E29B46D4F00992E96 /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A80F53D29B46D3E00992E96 /* Common.swift */; }; 5A8406B52609DF1B002B8B34 /* NSAttributedString+Writing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A8406B42609DF1B002B8B34 /* NSAttributedString+Writing.swift */; }; 5A8406B62609DF1B002B8B34 /* NSAttributedString+Writing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A8406B42609DF1B002B8B34 /* NSAttributedString+Writing.swift */; }; 5A8406BC2609DF2B002B8B34 /* ImageRelationship.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A8406BB2609DF2B002B8B34 /* ImageRelationship.swift */; }; @@ -40,7 +44,6 @@ 5A9F70B1223502D6008E967C /* DocX.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A9F70A3223502D6008E967C /* DocX.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5A9F70C222350300008E967C /* DocX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9F70BC22350300008E967C /* DocX.swift */; }; 5A9F70C522350300008E967C /* DocumentRoot.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9F70BF22350300008E967C /* DocumentRoot.swift */; }; - 5A9F70C72235031B008E967C /* blank.docx in Resources */ = {isa = PBXBuildFile; fileRef = 5A9F70C62235031B008E967C /* blank.docx */; }; 5A9F70F722350D12008E967C /* ParagraphElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9F70F622350D12008E967C /* ParagraphElement.swift */; }; 5A9F70F922350DDE008E967C /* AttributeElements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9F70F822350DDE008E967C /* AttributeElements.swift */; }; 5A9F70FB22354F1C008E967C /* RubyAnnotationElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A9F70FA22354F1C008E967C /* RubyAnnotationElement.swift */; }; @@ -74,10 +77,6 @@ 5AE8342722377A5B00E68343 /* NSUnderlineStyle+Elements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AE8341A22375D9E00E68343 /* NSUnderlineStyle+Elements.swift */; }; 5AE83428223781F600E68343 /* NSAttributedString+DocX.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AE833EB223683F000E68343 /* NSAttributedString+DocX.swift */; }; 5AE834292237822B00E68343 /* blank in Resources */ = {isa = PBXBuildFile; fileRef = 5AE833F2223688A400E68343 /* blank */; }; - 6D51921D29AD8B7A0043ECA8 /* DocXStyleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D51921C29AD8B7A0043ECA8 /* DocXStyleConfiguration.swift */; }; - 6D51921F29AD8C800043ECA8 /* DocXStyleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D51921C29AD8B7A0043ECA8 /* DocXStyleConfiguration.swift */; }; - 6D51922129AD8D320043ECA8 /* styles.xml in Resources */ = {isa = PBXBuildFile; fileRef = 6D51922029AD8D320043ECA8 /* styles.xml */; }; - 6D51922229AD8D320043ECA8 /* styles.xml in Resources */ = {isa = PBXBuildFile; fileRef = 6D51922029AD8D320043ECA8 /* styles.xml */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -100,13 +99,15 @@ /* Begin PBXFileReference section */ 5A0AD5CE26099AA200C146D3 /* NSTextAttachement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTextAttachement+Extensions.swift"; sourceTree = ""; }; 5A2E47BC29B4535200DFAC1F /* DocXStyleConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocXStyleConfiguration.swift; sourceTree = ""; }; - 5A2E47BF29B4536300DFAC1F /* ImageGeneration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageGeneration.swift; sourceTree = ""; }; - 5A2E47C229B453AB00DFAC1F /* styles.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = styles.xml; sourceTree = ""; }; 5A4FC7E827FEF5630063F796 /* AttributedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AttributedString.swift; sourceTree = ""; }; - 5A4FC7F727FFB4630063F796 /* lenna.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = lenna.md; sourceTree = ""; }; - 5A4FC7F927FFB46C0063F796 /* lenna.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = lenna.png; sourceTree = ""; }; - 5A4FC7FB27FFB6240063F796 /* Picture1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Picture1.png; path = DocXTests/Picture1.png; sourceTree = SOURCE_ROOT; }; 5A6B3DBE26D86D33009F6859 /* DocXOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocXOptions.swift; sourceTree = ""; }; + 5A80F53829B46D3E00992E96 /* ImageGeneration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ImageGeneration.swift; path = DocXTestsCommon/ImageGeneration.swift; sourceTree = SOURCE_ROOT; }; + 5A80F53929B46D3E00992E96 /* styles.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = styles.xml; path = DocXTestsCommon/styles.xml; sourceTree = SOURCE_ROOT; }; + 5A80F53A29B46D3E00992E96 /* Picture1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Picture1.png; path = DocXTestsCommon/Picture1.png; sourceTree = SOURCE_ROOT; }; + 5A80F53B29B46D3E00992E96 /* lenna.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = lenna.md; path = DocXTestsCommon/lenna.md; sourceTree = SOURCE_ROOT; }; + 5A80F53C29B46D3E00992E96 /* blank.docx */ = {isa = PBXFileReference; lastKnownFileType = file; name = blank.docx; path = DocXTestsCommon/blank.docx; sourceTree = SOURCE_ROOT; }; + 5A80F53D29B46D3E00992E96 /* Common.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Common.swift; path = DocXTestsCommon/Common.swift; sourceTree = SOURCE_ROOT; }; + 5A80F53E29B46D3E00992E96 /* lenna.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = lenna.png; path = DocXTestsCommon/lenna.png; sourceTree = SOURCE_ROOT; }; 5A8406B42609DF1B002B8B34 /* NSAttributedString+Writing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Writing.swift"; sourceTree = ""; }; 5A8406BB2609DF2B002B8B34 /* ImageRelationship.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageRelationship.swift; sourceTree = ""; }; 5A8406CC2609DF65002B8B34 /* Bundle+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Extensions.swift"; sourceTree = ""; }; @@ -121,7 +122,6 @@ 5A9F70BC22350300008E967C /* DocX.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocX.swift; sourceTree = ""; }; 5A9F70BE22350300008E967C /* NSAttributedString+DocX-macOS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+DocX-macOS.swift"; sourceTree = ""; }; 5A9F70BF22350300008E967C /* DocumentRoot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentRoot.swift; sourceTree = ""; }; - 5A9F70C62235031B008E967C /* blank.docx */ = {isa = PBXFileReference; lastKnownFileType = file; path = blank.docx; sourceTree = ""; }; 5A9F70F622350D12008E967C /* ParagraphElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParagraphElement.swift; sourceTree = ""; }; 5A9F70F822350DDE008E967C /* AttributeElements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeElements.swift; sourceTree = ""; }; 5A9F70FA22354F1C008E967C /* RubyAnnotationElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RubyAnnotationElement.swift; sourceTree = ""; }; @@ -140,7 +140,6 @@ 5AE834112237573F00E68343 /* FontElements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontElements.swift; sourceTree = ""; }; 5AE8341A22375D9E00E68343 /* NSUnderlineStyle+Elements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSUnderlineStyle+Elements.swift"; sourceTree = ""; }; 6D51921C29AD8B7A0043ECA8 /* DocXStyleConfiguration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocXStyleConfiguration.swift; sourceTree = ""; }; - 6D51922029AD8D320043ECA8 /* styles.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = styles.xml; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -213,9 +212,9 @@ 5A9F70A2223502D6008E967C /* DocX */ = { isa = PBXGroup; children = ( + 5AE833F2223688A400E68343 /* blank */, 5A907B862982A35800E1A581 /* DocXPageDefinition.swift */, 5A907B872982A35800E1A581 /* DocXStyleConfiguration.swift */, - 5AE833F2223688A400E68343 /* blank */, 5A9F70BF22350300008E967C /* DocumentRoot.swift */, 5A9F70BC22350300008E967C /* DocX.swift */, 5AC6C9B4260ACCEB0059F7B0 /* DocXWriting.swift */, @@ -248,13 +247,14 @@ 5A9F70AD223502D6008E967C /* DocXTests */ = { isa = PBXGroup; children = ( - 5A2E47C229B453AB00DFAC1F /* styles.xml */, - 5A2E47BF29B4536300DFAC1F /* ImageGeneration.swift */, - 5A4FC7F727FFB4630063F796 /* lenna.md */, - 5A4FC7F927FFB46C0063F796 /* lenna.png */, - 5A9F70C62235031B008E967C /* blank.docx */, + 5A80F53C29B46D3E00992E96 /* blank.docx */, + 5A80F53D29B46D3E00992E96 /* Common.swift */, + 5A80F53829B46D3E00992E96 /* ImageGeneration.swift */, + 5A80F53B29B46D3E00992E96 /* lenna.md */, + 5A80F53E29B46D3E00992E96 /* lenna.png */, + 5A80F53A29B46D3E00992E96 /* Picture1.png */, + 5A80F53929B46D3E00992E96 /* styles.xml */, 5A9F70AE223502D6008E967C /* DocXTests.swift */, - 6D51922029AD8D320043ECA8 /* styles.xml */, 5A9F70B0223502D6008E967C /* Info.plist */, ); path = DocXTests; @@ -263,7 +263,6 @@ 5AE833F922368A4500E68343 /* DocX-iOS-Tests */ = { isa = PBXGroup; children = ( - 5A4FC7FB27FFB6240063F796 /* Picture1.png */, 5AE833FA22368A4500E68343 /* DocX_iOS_Tests.swift */, 5AE833FC22368A4500E68343 /* Info.plist */, ); @@ -432,10 +431,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5A9F70C72235031B008E967C /* blank.docx in Resources */, - 5A2E47C329B453AB00DFAC1F /* styles.xml in Resources */, - 5A4FC7F827FFB4630063F796 /* lenna.md in Resources */, - 5A4FC7FA27FFB46C0063F796 /* lenna.png in Resources */, + 5A80F54329B46D3E00992E96 /* Picture1.png in Resources */, + 5A80F54529B46D3E00992E96 /* lenna.md in Resources */, + 5A80F54729B46D3E00992E96 /* blank.docx in Resources */, + 5A80F54B29B46D3E00992E96 /* lenna.png in Resources */, + 5A80F54129B46D3E00992E96 /* styles.xml in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -451,10 +451,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5A4FC7FC27FFB6240063F796 /* Picture1.png in Resources */, - 6D51922229AD8D320043ECA8 /* styles.xml in Resources */, - 5A4FC7FD27FFB6430063F796 /* lenna.md in Resources */, - 5A4FC7FE27FFB6460063F796 /* lenna.png in Resources */, + 5A80F54429B46D3E00992E96 /* Picture1.png in Resources */, + 5A80F54629B46D3E00992E96 /* lenna.md in Resources */, + 5A80F54829B46D3E00992E96 /* blank.docx in Resources */, + 5A80F54C29B46D3E00992E96 /* lenna.png in Resources */, + 5A80F54229B46D3E00992E96 /* styles.xml in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -469,7 +470,6 @@ 5AE834122237573F00E68343 /* FontElements.swift in Sources */, 5A9F711D223607A8008E967C /* NSAttributedString+Extensions.swift in Sources */, 5A8406CD2609DF65002B8B34 /* Bundle+Extensions.swift in Sources */, - 5A2E47BD29B4535200DFAC1F /* DocXStyleConfiguration.swift in Sources */, 5AC6C9B8260AD4CF0059F7B0 /* DocXWriter.swift in Sources */, 5AE833F12236861F00E68343 /* PlatformSpecific-macOS.swift in Sources */, 5A9F70F922350DDE008E967C /* AttributeElements.swift in Sources */, @@ -496,7 +496,8 @@ buildActionMask = 2147483647; files = ( 5A9F70AF223502D6008E967C /* DocXTests.swift in Sources */, - 5A2E47C029B4536300DFAC1F /* ImageGeneration.swift in Sources */, + 5A80F54929B46D3E00992E96 /* Common.swift in Sources */, + 5A80F53F29B46D3E00992E96 /* ImageGeneration.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -508,7 +509,6 @@ 5AE834132237573F00E68343 /* FontElements.swift in Sources */, 5AE833EF2236852500E68343 /* PlatformSpecific-iOS.swift in Sources */, 5A8406CE2609DF65002B8B34 /* Bundle+Extensions.swift in Sources */, - 5A2E47BE29B4535200DFAC1F /* DocXStyleConfiguration.swift in Sources */, 5AC6C9B9260AD4CF0059F7B0 /* DocXWriter.swift in Sources */, 5AE833D12236831500E68343 /* NSAttributedString+Extensions.swift in Sources */, 5AE833D52236831500E68343 /* AttributeElements.swift in Sources */, @@ -535,7 +535,8 @@ buildActionMask = 2147483647; files = ( 5AE8340422368ADC00E68343 /* DocX_iOS_Tests.swift in Sources */, - 5A2E47C129B4537D00DFAC1F /* ImageGeneration.swift in Sources */, + 5A80F54E29B46D4F00992E96 /* Common.swift in Sources */, + 5A80F54D29B46D4C00992E96 /* ImageGeneration.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/DocX/DocX.swift b/DocX/DocX.swift index cb9b277..d1a601b 100644 --- a/DocX/DocX.swift +++ b/DocX/DocX.swift @@ -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" diff --git a/DocX/DocXWriting.swift b/DocX/DocXWriting.swift index e6a31fd..f44cfed 100644 --- a/DocX/DocXWriting.swift +++ b/DocX/DocXWriting.swift @@ -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{ @@ -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{ @@ -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 diff --git a/DocX/ImageRelationship.swift b/DocX/ImageRelationship.swift index 61214ba..a5b4d17 100644 --- a/DocX/ImageRelationship.swift +++ b/DocX/ImageRelationship.swift @@ -19,6 +19,9 @@ struct ImageRelationship: DocumentRelationship{ let relationshipID:String let linkURL:URL let attachement:NSTextAttachment + let pageDefinition:PageDefinition? + + } extension ImageRelationship{ @@ -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"]) @@ -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) diff --git a/DocX/NSAttributedString+DocX-macOS.swift b/DocX/NSAttributedString+DocX-macOS.swift index 46c4778..26e99ef 100644 --- a/DocX/NSAttributedString+DocX-macOS.swift +++ b/DocX/NSAttributedString+DocX-macOS.swift @@ -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) diff --git a/DocX/NSAttributedString+Writing.swift b/DocX/NSAttributedString+Writing.swift index bed6aeb..fe6a679 100644 --- a/DocX/NSAttributedString+Writing.swift +++ b/DocX/NSAttributedString+Writing.swift @@ -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) diff --git a/DocX/NSTextAttachement+Extensions.swift b/DocX/NSTextAttachement+Extensions.swift index 53236c5..a2edc1b 100644 --- a/DocX/NSTextAttachement+Extensions.swift +++ b/DocX/NSTextAttachement+Extensions.swift @@ -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) } @@ -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 @@ -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] diff --git a/DocXTests/DocXTests.swift b/DocXTests/DocXTests.swift index 4d1c56e..c1e142d 100644 --- a/DocXTests/DocXTests.swift +++ b/DocXTests/DocXTests.swift @@ -11,32 +11,18 @@ import XCTest @testable import DocX import AppKit +#if SWIFT_PACKAGE +import DocXTestsCommon +#endif + #if(canImport(UniformTypeIdentifiers)) import UniformTypeIdentifiers #endif -class DocXTests: XCTestCase { +class DocXTests: XCTestCase, DocXTesting { - // XXX This currently only lists a small subset of possible errors - // It would be nice to list all possible errors here - enum TestError: Error { - // Error thrown when the expected link text isn't found in the given string - case couldNotFindLinkTitle - - // Error thrown when validating the docx fails - case validationFailed - } - - -#if SWIFT_PACKAGE - let bundle=Bundle.module -#else - let bundle=Bundle(for: DocXTests.self) -#endif - var tempURL:URL=URL(fileURLWithPath: "") - override func setUp() { let url=FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true) do{ @@ -60,46 +46,7 @@ class DocXTests: XCTestCase { } } - /// Returns a basename that can be used when exporting a docx - private func docxBasename(attributedString: NSAttributedString) -> String { - return UUID().uuidString + "_myDocument_\(attributedString.string.prefix(10))" - } - - /// This function tests writing a docx file using the *DocX* exporter - /// Optionally, options may be passed - func writeAndValidateDocX(attributedString: NSAttributedString, - options: DocXOptions = DocXOptions()) throws { - let url = self.tempURL.appendingPathComponent(docxBasename(attributedString: attributedString)).appendingPathExtension("docx") - try attributedString.writeDocX(to: url, options: options) - // Validate that writing was successful - try validateDocX(url: url) - } - - /// This function tests writing a docx file using the macOS builtin exporter - func writeAndValidateDocXUsingBuiltIn(attributedString: NSAttributedString) throws { - let url = self.tempURL.appendingPathComponent(docxBasename(attributedString: attributedString)).appendingPathExtension("docx") - try attributedString.writeDocX(to: url, useBuiltIn: true) - - // Validate that writing was successful - try validateDocX(url: url) - } - /// Performs a very simply validation of the docx file by reading it - /// in and making sure the document type is OOXML - func validateDocX(url: URL) throws { - // Read the string from the URL - var readAttributes:NSDictionary? - let _ = try NSAttributedString(url: url, options: [:], documentAttributes: &readAttributes) - - // Make sure we read the document attributes - guard let attributes = readAttributes as? [String:Any] else { - throw TestError.validationFailed - } - - // The document type should be OOXML - XCTAssertEqual(attributes[NSAttributedString.DocumentAttributeKey.documentType.rawValue] as! String, - NSAttributedString.DocumentType.officeOpenXML.rawValue) - } func testEscapedCharacters() throws { let string="\"You done messed up A'Aron!\" " @@ -364,7 +311,7 @@ Specifies the border displayed above a set of paragraphs which have the same set let longString = """ 1. 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=URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("Picture1.png") + let imageURL=try XCTUnwrap(bundle.url(forResource: "Picture1", withExtension: "png")) let imageData=try XCTUnwrap(Data(contentsOf: imageURL), "Image not found") let attachement=NSTextAttachment(data: imageData, ofType: kUTTypePNG as String) let attributed=NSAttributedString(string: longString, attributes: [.foregroundColor: NSColor.green]) @@ -379,7 +326,7 @@ Specifies the border displayed above a set of paragraphs which have the same set let longString = """ 1. 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=URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("Picture1.png") + let imageURL=try XCTUnwrap(bundle.url(forResource: "Picture1", withExtension: "png")) let imageData=try XCTUnwrap(Data(contentsOf: imageURL), "Image not found") let attachement=NSTextAttachment(data: imageData, ofType: kUTTypePNG as String) let attributed=NSMutableAttributedString(string: longString, attributes: [:]) @@ -395,7 +342,7 @@ Specifies the border displayed above a set of paragraphs which have the same set let longString = """ 1. 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\r. """ - let imageURL=URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("Picture1.png") + let imageURL=try XCTUnwrap(bundle.url(forResource: "Picture1", withExtension: "png")) let imageData=try XCTUnwrap(Data(contentsOf: imageURL), "Image not found") let attachement=NSTextAttachment(data: imageData, ofType: kUTTypePNG as String) let attributed=NSMutableAttributedString(string: longString, attributes: [:]) @@ -461,7 +408,7 @@ Specifies the border displayed above a set of paragraphs which have the same set let longString = """ 1. 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=URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("Picture1.png") + let imageURL=try XCTUnwrap(bundle.url(forResource: "Picture1", withExtension: "png")) let imageData=try XCTUnwrap(Data(contentsOf: imageURL), "Image not found") let attachement=NSTextAttachment(data: imageData, ofType: kUTTypePNG as String) let attributed=NSMutableAttributedString(string: longString, attributes: [:]) @@ -795,6 +742,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 diff --git a/DocXTests/Picture1.png b/DocXTests/Picture1.png deleted file mode 100644 index 71202fd..0000000 Binary files a/DocXTests/Picture1.png and /dev/null differ diff --git a/DocXTests/lenna.md b/DocXTests/lenna.md deleted file mode 100644 index 9252626..0000000 --- a/DocXTests/lenna.md +++ /dev/null @@ -1,3 +0,0 @@ -This is a **Markdown** *String*. -This is a [link](https://www.example.com). -![This is an image](lenna.png) \ No newline at end of file diff --git a/DocXTests/lenna.png b/DocXTests/lenna.png deleted file mode 100644 index 59ef68a..0000000 Binary files a/DocXTests/lenna.png and /dev/null differ diff --git a/DocXTestsCommon/Common.swift b/DocXTestsCommon/Common.swift new file mode 100644 index 0000000..06fc948 --- /dev/null +++ b/DocXTestsCommon/Common.swift @@ -0,0 +1,109 @@ +// +// Common.swift +// +// +// Created by Morten Bertz on 2023/03/05. +// + +import Foundation +import DocX +import XCTest + +// XXX This currently only lists a small subset of possible errors +// It would be nice to list all possible errors here +public enum TestError: Error { + // Error thrown when the expected link text isn't found in the given string + case couldNotFindLinkTitle + + // Error thrown when validating the docx fails + case validationFailed +} + +public protocol DocXTesting{ + + /// This function tests writing a docx file using the *DocX* exporter + /// Optionally, options may be passed + func writeAndValidateDocX(attributedString: NSAttributedString, + options: DocXOptions) throws + + /// This function tests writing a docx file using the macOS builtin exporter + func writeAndValidateDocXUsingBuiltIn(attributedString: NSAttributedString) throws + + /// Returns a basename that can be used when exporting a docx + func docxBasename(attributedString: NSAttributedString) -> String + + /// Performs a very simply validation of the docx file by reading it + /// in and making sure the document type is OOXML + func validateDocX(url: URL) throws + + /// The URL to store the generated .docx files + var tempURL:URL { get} + + /// The bundle where to find the test resources + var bundle:Bundle { get} + +} + + +extension DocXTesting{ + + public var bundle: Bundle { +#if SWIFT_PACKAGE + return Bundle.module +#elseif os(macOS) + return Bundle(for: DocXTests.self) +#else + return Bundle(for: DocX_iOS_Tests.self) +#endif + } + + + public func docxBasename(attributedString: NSAttributedString) -> String { + return UUID().uuidString + "_myDocument_\(attributedString.string.prefix(10))" + } + + + public func writeAndValidateDocX(attributedString: NSAttributedString, + options: DocXOptions = DocXOptions()) throws { + let url = self.tempURL.appendingPathComponent(docxBasename(attributedString: attributedString)).appendingPathExtension("docx") + try attributedString.writeDocX(to: url, options: options) + // Validate that writing was successful + try validateDocX(url: url) + } + + public func writeAndValidateDocXUsingBuiltIn(attributedString: NSAttributedString) throws { + let url = self.tempURL.appendingPathComponent(docxBasename(attributedString: attributedString)).appendingPathExtension("docx") + #if canImport(AppKit) + try attributedString.writeDocX(to: url, useBuiltIn: true) + #else + try attributedString.writeDocX(to: url) + #endif + + // Validate that writing was successful + try validateDocX(url: url) + } + +#if canImport(AppKit) + public func validateDocX(url: URL) throws { + // Read the string from the URL + var readAttributes:NSDictionary? + let _ = try NSAttributedString(url: url, options: [:], documentAttributes: &readAttributes) + + // Make sure we read the document attributes + guard let attributes = readAttributes as? [String:Any] else { + throw TestError.validationFailed + } + + // The document type should be OOXML + XCTAssertEqual(attributes[NSAttributedString.DocumentAttributeKey.documentType.rawValue] as! String, + NSAttributedString.DocumentType.officeOpenXML.rawValue) + } + +#else + public func validateDocX(url: URL) throws {} +#endif +} + + + + diff --git a/DocXTests/ImageGeneration.swift b/DocXTestsCommon/ImageGeneration.swift similarity index 96% rename from DocXTests/ImageGeneration.swift rename to DocXTestsCommon/ImageGeneration.swift index 36822f8..64b1789 100644 --- a/DocXTests/ImageGeneration.swift +++ b/DocXTestsCommon/ImageGeneration.swift @@ -23,7 +23,7 @@ fileprivate typealias UIColor = NSColor #endif @available(iOS 16.0, macOS 11.0, *) -class ImageGenerator{ +public class ImageGenerator{ enum ImageGenerationError: Error{ case unsupportedFileType @@ -32,11 +32,11 @@ class ImageGenerator{ let size:CGSize - init(size:CGSize){ + public init(size:CGSize){ self.size=size } - func generateImage(type:UTType) throws -> URL{ + public func generateImage(type:UTType) throws -> URL{ let outURL=FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString).appendingPathExtension(for: type) switch type{ case .pdf: diff --git a/DocX-iOS-Tests/Picture1.png b/DocXTestsCommon/Picture1.png similarity index 100% rename from DocX-iOS-Tests/Picture1.png rename to DocXTestsCommon/Picture1.png diff --git a/DocXTests/blank.docx b/DocXTestsCommon/blank.docx similarity index 100% rename from DocXTests/blank.docx rename to DocXTestsCommon/blank.docx diff --git a/DocX-iOS-Tests/lenna.md b/DocXTestsCommon/lenna.md similarity index 100% rename from DocX-iOS-Tests/lenna.md rename to DocXTestsCommon/lenna.md diff --git a/DocX-iOS-Tests/lenna.png b/DocXTestsCommon/lenna.png similarity index 100% rename from DocX-iOS-Tests/lenna.png rename to DocXTestsCommon/lenna.png diff --git a/DocXTests/styles.xml b/DocXTestsCommon/styles.xml similarity index 100% rename from DocXTests/styles.xml rename to DocXTestsCommon/styles.xml diff --git a/Package.swift b/Package.swift index 35fd4a6..0c239d2 100644 --- a/Package.swift +++ b/Package.swift @@ -40,20 +40,24 @@ let package = Package( linkerSettings: nil ), + .target(name: "DocXTestsCommon", + dependencies: ["DocX"], + path: "DocXTestsCommon", + resources: [.copy("blank.docx"), .copy("Picture1.png"), .copy("lenna.png"), .copy("lenna.md"), .copy("styles.xml")] + ), + .testTarget( name: "DocXTests", - dependencies: ["DocX"], + dependencies: ["DocX", "DocXTestsCommon"], path: "DocXTests", - exclude: ["Info.plist"], - resources: [.copy("blank.docx"), .copy("Picture1.png"), .copy("lenna.png"), .copy("lenna.md"), .copy("styles.xml")] + exclude: ["Info.plist"] ), .testTarget( name: "DocX-iOS-Tests", - dependencies: ["DocX"], + dependencies: ["DocX", "DocXTestsCommon"], path: "DocX-iOS-Tests", - exclude: ["Info.plist"], - resources: [.copy("Picture1.png"),.copy("lenna.png"), .copy("lenna.md")] + exclude: ["Info.plist"] ) ]