fastlane-plugin-wpmreleasetoolkit 2.0.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_localize_libs_action.rb +8 -3
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_betabuild_prechecks.rb +8 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_bump_version_release.rb +11 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_codefreeze_prechecks.rb +10 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/buildkite_trigger_build_action.rb +90 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/comment_on_pr.rb +89 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_action.rb +2 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/common/gp_downloadmetadata_action.rb +1 -1
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_betabuild_prechecks.rb +8 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_bump_version_release.rb +10 -5
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_codefreeze_prechecks.rb +10 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_download_strings_files_from_glotpress.rb +113 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_generate_strings_file_from_code.rb +115 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb +5 -5
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_localize_project.rb +6 -7
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_strings_files.rb +75 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_git_helper.rb +0 -20
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/android/android_localize_helper.rb +8 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb +3 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/github_helper.rb +48 -8
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb +93 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_git_helper.rb +3 -2
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb +108 -173
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_linter_helper.rb +207 -0
- data/lib/fastlane/plugin/wpmreleasetoolkit/helper/promo_screenshots_helper.rb +3 -3
- data/lib/fastlane/plugin/wpmreleasetoolkit/models/file_reference.rb +1 -4
- data/lib/fastlane/plugin/wpmreleasetoolkit/version.rb +1 -1
- metadata +26 -39
- data/bin/drawText +0 -20
- data/ext/drawText/drawText/Assets/style.css +0 -1
- data/ext/drawText/drawText/CoreTextStack.swift +0 -113
- data/ext/drawText/drawText/Helpers/CommandLineHelpers.swift +0 -36
- data/ext/drawText/drawText/Helpers/Extensions.swift +0 -27
- data/ext/drawText/drawText/Helpers/FileSystemHelper.swift +0 -24
- data/ext/drawText/drawText/Stylesheet.swift +0 -48
- data/ext/drawText/drawText/TextImage.swift +0 -100
- data/ext/drawText/drawText/main.swift +0 -61
- data/ext/drawText/drawText Tests/DigitParsingTests.swift +0 -21
- data/ext/drawText/drawText Tests/ExtensionsTests.swift +0 -5
- data/ext/drawText/drawText Tests/Info.plist +0 -22
- data/ext/drawText/drawText Tests/StylesheetTests.swift +0 -31
- data/ext/drawText/drawText Tests/Test Cases/default-stylesheet.txt +0 -10
- data/ext/drawText/drawText Tests/Test Cases/external-styles-sample.css +0 -3
- data/ext/drawText/drawText Tests/Test Cases/external-styles-test.txt +0 -13
- data/ext/drawText/drawText Tests/Test Cases/large-text-block.txt +0 -1
- data/ext/drawText/drawText Tests/Test Cases/regular-text-block.txt +0 -2
- data/ext/drawText/drawText Tests/Test Cases/rtl-text-block.txt +0 -2
- data/ext/drawText/drawText Tests/Test Cases/text-size-adjustment-test.txt +0 -10
- data/ext/drawText/drawText Tests/TextImageTests.swift +0 -99
- data/ext/drawText/drawText Tests/drawText_Tests.swift +0 -14
- data/ext/drawText/drawText.xcodeproj/project.pbxproj +0 -508
- data/ext/drawText/drawText.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
- data/ext/drawText/drawText.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- data/ext/drawText/drawText.xcodeproj/xcshareddata/xcschemes/drawText Tests.xcscheme +0 -57
- data/ext/drawText/drawText.xcodeproj/xcshareddata/xcschemes/drawText.xcscheme +0 -109
- data/ext/drawText/extconf.rb +0 -36
- data/ext/drawText/makefile.example +0 -8
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_merge_translators_strings.rb +0 -106
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_metadata.rb +0 -52
- data/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_translators_strings.rb +0 -93
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-wpmreleasetoolkit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lorenzo Mattei
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: diffy
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '4.18'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: buildkit
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.4'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.4'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: git
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -340,43 +354,12 @@ dependencies:
|
|
340
354
|
version: '1.10'
|
341
355
|
description:
|
342
356
|
email: lore.mattei@gmail.com
|
343
|
-
executables:
|
344
|
-
|
345
|
-
extensions:
|
346
|
-
- ext/drawText/extconf.rb
|
357
|
+
executables: []
|
358
|
+
extensions: []
|
347
359
|
extra_rdoc_files: []
|
348
360
|
files:
|
349
361
|
- LICENSE
|
350
362
|
- README.md
|
351
|
-
- bin/drawText
|
352
|
-
- ext/drawText/drawText Tests/DigitParsingTests.swift
|
353
|
-
- ext/drawText/drawText Tests/ExtensionsTests.swift
|
354
|
-
- ext/drawText/drawText Tests/Info.plist
|
355
|
-
- ext/drawText/drawText Tests/StylesheetTests.swift
|
356
|
-
- ext/drawText/drawText Tests/Test Cases/default-stylesheet.txt
|
357
|
-
- ext/drawText/drawText Tests/Test Cases/external-styles-sample.css
|
358
|
-
- ext/drawText/drawText Tests/Test Cases/external-styles-test.txt
|
359
|
-
- ext/drawText/drawText Tests/Test Cases/large-text-block.txt
|
360
|
-
- ext/drawText/drawText Tests/Test Cases/regular-text-block.txt
|
361
|
-
- ext/drawText/drawText Tests/Test Cases/rtl-text-block.txt
|
362
|
-
- ext/drawText/drawText Tests/Test Cases/text-size-adjustment-test.txt
|
363
|
-
- ext/drawText/drawText Tests/TextImageTests.swift
|
364
|
-
- ext/drawText/drawText Tests/drawText_Tests.swift
|
365
|
-
- ext/drawText/drawText.xcodeproj/project.pbxproj
|
366
|
-
- ext/drawText/drawText.xcodeproj/project.xcworkspace/contents.xcworkspacedata
|
367
|
-
- ext/drawText/drawText.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
|
368
|
-
- ext/drawText/drawText.xcodeproj/xcshareddata/xcschemes/drawText Tests.xcscheme
|
369
|
-
- ext/drawText/drawText.xcodeproj/xcshareddata/xcschemes/drawText.xcscheme
|
370
|
-
- ext/drawText/drawText/Assets/style.css
|
371
|
-
- ext/drawText/drawText/CoreTextStack.swift
|
372
|
-
- ext/drawText/drawText/Helpers/CommandLineHelpers.swift
|
373
|
-
- ext/drawText/drawText/Helpers/Extensions.swift
|
374
|
-
- ext/drawText/drawText/Helpers/FileSystemHelper.swift
|
375
|
-
- ext/drawText/drawText/Stylesheet.swift
|
376
|
-
- ext/drawText/drawText/TextImage.swift
|
377
|
-
- ext/drawText/drawText/main.swift
|
378
|
-
- ext/drawText/extconf.rb
|
379
|
-
- ext/drawText/makefile.example
|
380
363
|
- lib/fastlane/plugin/wpmreleasetoolkit.rb
|
381
364
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/README.md
|
382
365
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/an_localize_libs_action.rb
|
@@ -400,14 +383,14 @@ files:
|
|
400
383
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_app_version.rb
|
401
384
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_get_release_version.rb
|
402
385
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_hotifx_prechecks.rb
|
403
|
-
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_merge_translators_strings.rb
|
404
386
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_tag_build.rb
|
405
|
-
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_metadata.rb
|
406
387
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_update_release_notes.rb
|
388
|
+
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/buildkite_trigger_build_action.rb
|
407
389
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_for_toolkit_updates_action.rb
|
408
390
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/check_translation_progress.rb
|
409
391
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/circleci_trigger_job_action.rb
|
410
392
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/close_milestone_action.rb
|
393
|
+
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/comment_on_pr.rb
|
411
394
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_new_milestone_action.rb
|
412
395
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_action.rb
|
413
396
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/common/extract_release_notes_for_version_action.rb
|
@@ -437,15 +420,17 @@ files:
|
|
437
420
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_codefreeze_prechecks.rb
|
438
421
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_completecodefreeze_prechecks.rb
|
439
422
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_current_branch_is_hotfix.rb
|
423
|
+
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_download_strings_files_from_glotpress.rb
|
440
424
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_final_tag.rb
|
441
425
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_finalize_prechecks.rb
|
426
|
+
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_generate_strings_file_from_code.rb
|
442
427
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_app_version.rb
|
443
428
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_build_version.rb
|
444
429
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_get_store_app_sizes.rb
|
445
430
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_hotifx_prechecks.rb
|
446
431
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_lint_localizations.rb
|
447
432
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_localize_project.rb
|
448
|
-
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/
|
433
|
+
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_merge_strings_files.rb
|
449
434
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_tag_build.rb
|
450
435
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata.rb
|
451
436
|
- lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_update_metadata_source.rb
|
@@ -462,9 +447,11 @@ files:
|
|
462
447
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/git_helper.rb
|
463
448
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/github_helper.rb
|
464
449
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/glotpress_helper.rb
|
450
|
+
- lib/fastlane/plugin/wpmreleasetoolkit/helper/interactive_prompt_reminder.rb
|
465
451
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_adc_app_sizes_helper.rb
|
466
452
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_git_helper.rb
|
467
453
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_helper.rb
|
454
|
+
- lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_l10n_linter_helper.rb
|
468
455
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/ios/ios_version_helper.rb
|
469
456
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/metadata_download_helper.rb
|
470
457
|
- lib/fastlane/plugin/wpmreleasetoolkit/helper/metadata_update_helper.rb
|
@@ -492,7 +479,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
492
479
|
- !ruby/object:Gem::Version
|
493
480
|
version: '0'
|
494
481
|
requirements: []
|
495
|
-
rubygems_version: 3.
|
482
|
+
rubygems_version: 3.1.6
|
496
483
|
signing_key:
|
497
484
|
specification_version: 4
|
498
485
|
summary: GitHub helper functions
|
data/bin/drawText
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'rake'
|
4
|
-
require 'os'
|
5
|
-
require 'pathname'
|
6
|
-
|
7
|
-
abort 'Fatal Error: `drawText` can only be run on macOS.' unless OS.mac?
|
8
|
-
|
9
|
-
plugin_path = Pathname(__FILE__).dirname.parent
|
10
|
-
executable_path = plugin_path + 'lib/drawText'
|
11
|
-
|
12
|
-
# Run the script
|
13
|
-
allArgs = ARGV.map do |arg|
|
14
|
-
argParts = arg.split('=')
|
15
|
-
argParts[1] = '"' + argParts[1] + '"'
|
16
|
-
|
17
|
-
argParts.join('=')
|
18
|
-
end.join(' ')
|
19
|
-
|
20
|
-
exec("#{executable_path} #{allArgs}")
|
@@ -1 +0,0 @@
|
|
1
|
-
|
@@ -1,113 +0,0 @@
|
|
1
|
-
import Cocoa
|
2
|
-
import CoreGraphics
|
3
|
-
|
4
|
-
struct CoreTextStack {
|
5
|
-
|
6
|
-
let layoutManager: NSLayoutManager
|
7
|
-
let textContainer: NSTextContainer
|
8
|
-
var textStorage: NSTextStorage
|
9
|
-
|
10
|
-
let frameSize: CGSize
|
11
|
-
var alignment: NSTextAlignment
|
12
|
-
|
13
|
-
enum Errors: Error {
|
14
|
-
case unableToInitializeTextStorage
|
15
|
-
}
|
16
|
-
|
17
|
-
init(html: Data, size: CGSize, alignment: NSTextAlignment = .natural) throws {
|
18
|
-
|
19
|
-
guard let textStorage = NSTextStorage(html: html, documentAttributes: nil) else {
|
20
|
-
throw Errors.unableToInitializeTextStorage
|
21
|
-
}
|
22
|
-
|
23
|
-
///Storage
|
24
|
-
self.textStorage = textStorage
|
25
|
-
self.textContainer = NSTextContainer(size: size)
|
26
|
-
self.layoutManager = NSLayoutManager()
|
27
|
-
self.frameSize = size
|
28
|
-
self.alignment = alignment
|
29
|
-
|
30
|
-
/// Setup
|
31
|
-
layoutManager.addTextContainer(textContainer)
|
32
|
-
textStorage.addLayoutManager(layoutManager)
|
33
|
-
|
34
|
-
/// Configuration
|
35
|
-
textContainer.lineFragmentPadding = 0
|
36
|
-
textContainer.lineBreakMode = .byWordWrapping
|
37
|
-
layoutManager.usesFontLeading = true
|
38
|
-
layoutManager.typesetter.lineFragmentPadding = 0
|
39
|
-
|
40
|
-
applyParagraphStyles()
|
41
|
-
}
|
42
|
-
|
43
|
-
func setFontSize(_ size: CGFloat) {
|
44
|
-
|
45
|
-
/// Process each attribute run separately so that if the first word is bold,
|
46
|
-
/// it doesn't make all of the words bold.
|
47
|
-
textStorage.attributeRuns.forEach {
|
48
|
-
|
49
|
-
let range = NSRange(location: 0, length: $0.length)
|
50
|
-
|
51
|
-
$0.font = NSFont(name: $0.font!.fontName, size: size)
|
52
|
-
$0.edited(.editedAttributes, range: range, changeInLength: 0)
|
53
|
-
$0.fixAttributes(in: range)
|
54
|
-
}
|
55
|
-
|
56
|
-
applyParagraphStyles()
|
57
|
-
}
|
58
|
-
|
59
|
-
mutating func setAlignment(_ alignment: NSTextAlignment) {
|
60
|
-
self.alignment = alignment
|
61
|
-
applyParagraphStyles()
|
62
|
-
}
|
63
|
-
|
64
|
-
func applyParagraphStyles() {
|
65
|
-
|
66
|
-
let paragraphStyle = NSMutableParagraphStyle()
|
67
|
-
paragraphStyle.paragraphSpacingBefore = 0
|
68
|
-
paragraphStyle.paragraphSpacing = 0
|
69
|
-
paragraphStyle.alignment = self.alignment
|
70
|
-
|
71
|
-
textStorage.addAttributes([
|
72
|
-
NSAttributedString.Key.paragraphStyle: paragraphStyle
|
73
|
-
], range: range)
|
74
|
-
textStorage.edited(.editedAttributes, range: range, changeInLength: 0)
|
75
|
-
textStorage.fixAttributes(in: range)
|
76
|
-
}
|
77
|
-
|
78
|
-
func draw(inContext context: NSGraphicsContext) throws -> CGImage {
|
79
|
-
NSGraphicsContext.saveGraphicsState()
|
80
|
-
|
81
|
-
/// Without the transform, text is drawn upside down and backwards because of
|
82
|
-
/// macOS' flipped coordinate system.
|
83
|
-
NSGraphicsContext.current = NSGraphicsContext(cgContext: context.cgContext, flipped: true)
|
84
|
-
let transform = NSAffineTransform()
|
85
|
-
transform.scaleX(by: 1, yBy: -1)
|
86
|
-
transform.translateX(by: 0, yBy: frameSize.height * -1)
|
87
|
-
transform.concat()
|
88
|
-
|
89
|
-
layoutManager.drawGlyphs(forGlyphRange: range, at: .zero)
|
90
|
-
|
91
|
-
guard let image = context.cgContext.makeImage() else {
|
92
|
-
throw TextImageProcessingError(kind: .unableToDrawImage)
|
93
|
-
}
|
94
|
-
NSGraphicsContext.restoreGraphicsState()
|
95
|
-
|
96
|
-
return image
|
97
|
-
}
|
98
|
-
|
99
|
-
/// `fits` is used to determine whether we should shrink the font size in order to fit the text in
|
100
|
-
/// a smaller box. Turns out the best way to do that is to check whether all the characters fit into
|
101
|
-
/// the textContainer, rather than trying to measure the drawing area.
|
102
|
-
var fits: Bool {
|
103
|
-
|
104
|
-
let textStorageLength = textStorage.length
|
105
|
-
let textContainerRange = layoutManager.glyphRange(for: textContainer)
|
106
|
-
|
107
|
-
return textStorageLength == textContainerRange.length
|
108
|
-
}
|
109
|
-
|
110
|
-
var range: NSRange {
|
111
|
-
return layoutManager.glyphRange(for: textContainer)
|
112
|
-
}
|
113
|
-
}
|
@@ -1,36 +0,0 @@
|
|
1
|
-
import Foundation
|
2
|
-
|
3
|
-
func readCommandLineArguments() -> [String : String] {
|
4
|
-
|
5
|
-
return CommandLine.arguments
|
6
|
-
.filter{ $0.contains("=") }
|
7
|
-
.reduce([String : String](), { (dictionary, argument) -> [String : String] in
|
8
|
-
|
9
|
-
var dictionary = dictionary //shadow for mutability
|
10
|
-
|
11
|
-
var parts = argument.split(separator: "=")
|
12
|
-
dictionary[String(parts.remove(at: 0))] = parts.joined(separator: "=")
|
13
|
-
|
14
|
-
return dictionary
|
15
|
-
})
|
16
|
-
}
|
17
|
-
|
18
|
-
func printUsageAndExit() -> Never {
|
19
|
-
print("""
|
20
|
-
Usage: ./draw-text
|
21
|
-
html={file path or quotes-enclosed HTML string [required]}
|
22
|
-
maxWidth={ integer [required] }
|
23
|
-
maxHeight={ integer [required] }
|
24
|
-
fontSize={ CSS-size compatible string [default = 12px }
|
25
|
-
color={ color or hex code [default = white] }
|
26
|
-
align={ CSS text-alignment value [default = center] }
|
27
|
-
stylesheet={ File Path to a custom stylesheet [default = none] }
|
28
|
-
""")
|
29
|
-
exit(1)
|
30
|
-
}
|
31
|
-
|
32
|
-
func printError(_ string: String) {
|
33
|
-
let redColor = "\u{001B}[0;31m"
|
34
|
-
let endColor = "\u{001B}[0;m"
|
35
|
-
fputs("\(redColor)Error: \(string)\(endColor)\n", stderr)
|
36
|
-
}
|
@@ -1,27 +0,0 @@
|
|
1
|
-
import Foundation
|
2
|
-
import Cocoa
|
3
|
-
|
4
|
-
extension String {
|
5
|
-
var digits: Int? {
|
6
|
-
let digits = self
|
7
|
-
.components(separatedBy: CharacterSet.decimalDigits.inverted)
|
8
|
-
.joined()
|
9
|
-
|
10
|
-
return Int(digits)
|
11
|
-
}
|
12
|
-
|
13
|
-
var withNewlinesConvertedToBreakTags: String {
|
14
|
-
return self.replacingOccurrences(of: "\n", with: "<br />")
|
15
|
-
}
|
16
|
-
}
|
17
|
-
|
18
|
-
extension NSTextAlignment {
|
19
|
-
static func fromString(_ string: String) -> NSTextAlignment {
|
20
|
-
switch string.lowercased() {
|
21
|
-
case "left": return .left
|
22
|
-
case "center": return .center
|
23
|
-
case "right": return .right
|
24
|
-
default: return .natural
|
25
|
-
}
|
26
|
-
}
|
27
|
-
}
|
@@ -1,24 +0,0 @@
|
|
1
|
-
import Foundation
|
2
|
-
import Cocoa
|
3
|
-
|
4
|
-
struct File {
|
5
|
-
|
6
|
-
static func write(image: CGImage, toFileAtPath path: String) {
|
7
|
-
|
8
|
-
/// Turn it into a `png`
|
9
|
-
let rep = NSBitmapImageRep(cgImage: image)
|
10
|
-
let pngData = rep.representation(using: .png, properties: [:])
|
11
|
-
|
12
|
-
/// Then write it out to the provided path
|
13
|
-
do {
|
14
|
-
let pathString = NSString(string: path).expandingTildeInPath
|
15
|
-
let output = NSURL(fileURLWithPath: pathString)
|
16
|
-
|
17
|
-
try pngData?.write(to: output as URL)
|
18
|
-
}
|
19
|
-
catch let err {
|
20
|
-
printError("Unable to write image to \(path): \(err.localizedDescription)")
|
21
|
-
exit(6)
|
22
|
-
}
|
23
|
-
}
|
24
|
-
}
|
@@ -1,48 +0,0 @@
|
|
1
|
-
import Cocoa
|
2
|
-
|
3
|
-
struct Stylesheet {
|
4
|
-
|
5
|
-
var contents: String = ""
|
6
|
-
var externalStyles = "/* EXTERNAL STYLES */"
|
7
|
-
|
8
|
-
private let color: String
|
9
|
-
|
10
|
-
init(color: String, fontSize: Int) {
|
11
|
-
self.fontSize = fontSize
|
12
|
-
self.color = color
|
13
|
-
|
14
|
-
update()
|
15
|
-
}
|
16
|
-
|
17
|
-
mutating func updateWith(filePath: String) {
|
18
|
-
|
19
|
-
guard
|
20
|
-
FileManager.default.fileExists(atPath: filePath),
|
21
|
-
let fileContents = FileManager.default.contents(atPath: filePath),
|
22
|
-
let externalStyles = String(bytes: fileContents, encoding: .utf8) else {
|
23
|
-
return
|
24
|
-
}
|
25
|
-
|
26
|
-
self.externalStyles = externalStyles
|
27
|
-
update()
|
28
|
-
}
|
29
|
-
|
30
|
-
var fontSize: Int {
|
31
|
-
didSet { update() }
|
32
|
-
}
|
33
|
-
|
34
|
-
fileprivate mutating func update() {
|
35
|
-
self.contents = """
|
36
|
-
<style>
|
37
|
-
* {
|
38
|
-
padding: 0;
|
39
|
-
margin: 0;
|
40
|
-
color: \(color);
|
41
|
-
font-size: \(fontSize)px;
|
42
|
-
}
|
43
|
-
|
44
|
-
\(externalStyles)
|
45
|
-
</style>
|
46
|
-
""".trimmingCharacters(in: .whitespacesAndNewlines)
|
47
|
-
}
|
48
|
-
}
|
@@ -1,100 +0,0 @@
|
|
1
|
-
import Foundation
|
2
|
-
import Cocoa
|
3
|
-
import CoreText
|
4
|
-
|
5
|
-
class TextImage {
|
6
|
-
|
7
|
-
var color = "white"
|
8
|
-
var fontSize = 12 {
|
9
|
-
didSet {
|
10
|
-
stylesheet.fontSize = self.fontSize
|
11
|
-
}
|
12
|
-
}
|
13
|
-
var alignment: NSTextAlignment = .natural
|
14
|
-
|
15
|
-
lazy var stylesheet = Stylesheet(color: self.color, fontSize: self.fontSize)
|
16
|
-
|
17
|
-
internal var html: String = ""
|
18
|
-
|
19
|
-
internal let drawingOptions: NSString.DrawingOptions = [
|
20
|
-
.usesLineFragmentOrigin,
|
21
|
-
.usesFontLeading
|
22
|
-
]
|
23
|
-
|
24
|
-
init(string: String) throws {
|
25
|
-
|
26
|
-
if isValidFilePath(path: string) {
|
27
|
-
html = try contentsOfFile(at: string).trimmingCharacters(in: .whitespacesAndNewlines)
|
28
|
-
}
|
29
|
-
else {
|
30
|
-
html = string
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
func draw(inSize size: CGSize) throws -> CGImage? {
|
35
|
-
let coreTextStack = try CoreTextStack(html: self.getHTMLData(), size: size, alignment: alignment)
|
36
|
-
var fontSize = stylesheet.fontSize
|
37
|
-
|
38
|
-
while(!coreTextStack.fits) {
|
39
|
-
fontSize -= 1
|
40
|
-
coreTextStack.setFontSize(CGFloat(fontSize))
|
41
|
-
}
|
42
|
-
|
43
|
-
return try coreTextStack.draw(inContext: try self.graphicsContext(forSize: size))
|
44
|
-
}
|
45
|
-
|
46
|
-
internal func getHTMLData(withConvertedNewlines: Bool = true) -> Data {
|
47
|
-
let html = withConvertedNewlines ? self.html.withNewlinesConvertedToBreakTags : self.html
|
48
|
-
let fullString = stylesheet.contents + html
|
49
|
-
return fullString.data(using: .utf16)!
|
50
|
-
}
|
51
|
-
|
52
|
-
func isValidFilePath(path: String) -> Bool {
|
53
|
-
return FileManager.default.fileExists(atPath: path)
|
54
|
-
}
|
55
|
-
|
56
|
-
private func contentsOfFile(at path: String) throws -> String {
|
57
|
-
return try String(contentsOfFile: path, encoding: .utf8)
|
58
|
-
}
|
59
|
-
|
60
|
-
private func graphicsContext(forSize size: CGSize) throws -> NSGraphicsContext {
|
61
|
-
|
62
|
-
let canvas = NSBitmapImageRep(
|
63
|
-
bitmapDataPlanes: nil,
|
64
|
-
pixelsWide: Int(size.width),
|
65
|
-
pixelsHigh: Int(size.height),
|
66
|
-
bitsPerSample: 8,
|
67
|
-
samplesPerPixel: 4,
|
68
|
-
hasAlpha: true,
|
69
|
-
isPlanar: false,
|
70
|
-
colorSpaceName: .calibratedRGB,
|
71
|
-
bitmapFormat: .alphaFirst,
|
72
|
-
bytesPerRow: 0,
|
73
|
-
bitsPerPixel: 0
|
74
|
-
)!
|
75
|
-
|
76
|
-
/// Set up the graphics context
|
77
|
-
guard let context = NSGraphicsContext(bitmapImageRep: canvas) else {
|
78
|
-
throw TextImageProcessingError(kind: .unableToInitializeGraphicsContext)
|
79
|
-
}
|
80
|
-
|
81
|
-
return context
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
struct TextImageProcessingError: Error {
|
86
|
-
enum ErrorKind: Int {
|
87
|
-
case unableToInitializeGraphicsContext
|
88
|
-
case unableToDrawImage
|
89
|
-
case unableToReadHTMLString
|
90
|
-
}
|
91
|
-
|
92
|
-
let kind: ErrorKind
|
93
|
-
var localizedDescription: String {
|
94
|
-
switch self.kind {
|
95
|
-
case .unableToInitializeGraphicsContext: return "Unable to initialize graphics context"
|
96
|
-
case .unableToDrawImage: return "Unable to draw image"
|
97
|
-
case .unableToReadHTMLString: return "Unable to read input HTML string. It may not be valid HTML"
|
98
|
-
}
|
99
|
-
}
|
100
|
-
}
|
@@ -1,61 +0,0 @@
|
|
1
|
-
import Foundation
|
2
|
-
import Cocoa
|
3
|
-
|
4
|
-
let args = readCommandLineArguments()
|
5
|
-
|
6
|
-
// Read the HTML string out of the args. This can either be raw HTML, or a path to an HTML file
|
7
|
-
guard let htmlString = args["html"] else {
|
8
|
-
printError("Unable to read HTML string")
|
9
|
-
printUsageAndExit()
|
10
|
-
}
|
11
|
-
|
12
|
-
guard let maxWidthString = args["maxWidth"] else {
|
13
|
-
printError("Missing maxWidth argument")
|
14
|
-
printUsageAndExit()
|
15
|
-
}
|
16
|
-
|
17
|
-
guard let maxHeightString = args["maxHeight"] else {
|
18
|
-
printError("Missing maxHeight argument")
|
19
|
-
printUsageAndExit()
|
20
|
-
}
|
21
|
-
|
22
|
-
guard let maxWidth = Int(maxWidthString) else {
|
23
|
-
printError("maxWidth must be an integer")
|
24
|
-
printUsageAndExit()
|
25
|
-
}
|
26
|
-
|
27
|
-
guard let maxHeight = Int(maxHeightString) else {
|
28
|
-
printError("maxHeight must be an integer")
|
29
|
-
printUsageAndExit()
|
30
|
-
}
|
31
|
-
|
32
|
-
do {
|
33
|
-
let textImage = try TextImage(string: htmlString)
|
34
|
-
|
35
|
-
if let color = args["color"] {
|
36
|
-
textImage.color = color
|
37
|
-
}
|
38
|
-
|
39
|
-
if let fontSize = args["fontSize"]?.digits {
|
40
|
-
textImage.fontSize = fontSize
|
41
|
-
}
|
42
|
-
|
43
|
-
if let alignment = args["alignment"] {
|
44
|
-
textImage.alignment = NSTextAlignment.fromString(alignment)
|
45
|
-
}
|
46
|
-
|
47
|
-
if let stylesheetPath = args["stylesheet"] {
|
48
|
-
textImage.stylesheet.updateWith(filePath: stylesheetPath)
|
49
|
-
}
|
50
|
-
|
51
|
-
let outputPath = args["output"] ?? "output.png"
|
52
|
-
|
53
|
-
let size = CGSize(width: maxWidth, height: maxHeight)
|
54
|
-
let image = try textImage.draw(inSize: size)!
|
55
|
-
|
56
|
-
File.write(image: image, toFileAtPath: outputPath)
|
57
|
-
}
|
58
|
-
catch let err {
|
59
|
-
print(err.localizedDescription)
|
60
|
-
exit(1)
|
61
|
-
}
|
@@ -1,21 +0,0 @@
|
|
1
|
-
import XCTest
|
2
|
-
|
3
|
-
class DigitParsingTests: XCTestCase {
|
4
|
-
|
5
|
-
func testThatParsingMixedContentWorks() {
|
6
|
-
XCTAssert("$10".digits == 10)
|
7
|
-
}
|
8
|
-
|
9
|
-
func testThatParsingUnmixedContentWorks() {
|
10
|
-
XCTAssert("10".digits == 10)
|
11
|
-
}
|
12
|
-
|
13
|
-
// Yes, this is kinda weird, but CSS + decimal numbers is generally a bad idea anyway
|
14
|
-
func testThatParsingDecimalContentWorks() {
|
15
|
-
XCTAssert("10.0".digits == 100)
|
16
|
-
}
|
17
|
-
|
18
|
-
func testThatParsingNonDigitContentWorks() {
|
19
|
-
XCTAssert("Foo".digits == nil)
|
20
|
-
}
|
21
|
-
}
|
@@ -1,22 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
-
<plist version="1.0">
|
4
|
-
<dict>
|
5
|
-
<key>CFBundleDevelopmentRegion</key>
|
6
|
-
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
7
|
-
<key>CFBundleExecutable</key>
|
8
|
-
<string>$(EXECUTABLE_NAME)</string>
|
9
|
-
<key>CFBundleIdentifier</key>
|
10
|
-
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
11
|
-
<key>CFBundleInfoDictionaryVersion</key>
|
12
|
-
<string>6.0</string>
|
13
|
-
<key>CFBundleName</key>
|
14
|
-
<string>$(PRODUCT_NAME)</string>
|
15
|
-
<key>CFBundlePackageType</key>
|
16
|
-
<string>BNDL</string>
|
17
|
-
<key>CFBundleShortVersionString</key>
|
18
|
-
<string>1.0</string>
|
19
|
-
<key>CFBundleVersion</key>
|
20
|
-
<string>1</string>
|
21
|
-
</dict>
|
22
|
-
</plist>
|
@@ -1,31 +0,0 @@
|
|
1
|
-
import XCTest
|
2
|
-
|
3
|
-
class StylesheetTests: DrawTextTest {
|
4
|
-
|
5
|
-
func testThatDefaultStylesheetProducesValidOutput() {
|
6
|
-
let stylesheet = Stylesheet(color: "black", fontSize: 10)
|
7
|
-
XCTAssert(stylesheet.contents == getTestCase(named: "default-stylesheet"))
|
8
|
-
}
|
9
|
-
|
10
|
-
func testThatStylesheetCanChangeFontSizes() {
|
11
|
-
var stylesheet = Stylesheet(color: "black", fontSize: 10)
|
12
|
-
stylesheet.fontSize -= 1
|
13
|
-
XCTAssert(stylesheet.contents == getTestCase(named: "text-size-adjustment-test"))
|
14
|
-
}
|
15
|
-
|
16
|
-
func testThatStylesheetExternalStylesCanBeUpdated() {
|
17
|
-
var stylesheet = Stylesheet(color: "black", fontSize: 10)
|
18
|
-
stylesheet.updateWith(filePath: getTestCaseURLForAsset(named: "external-styles-sample", extension: "css").path)
|
19
|
-
XCTAssert(stylesheet.contents == getTestCase(named: "external-styles-test"))
|
20
|
-
}
|
21
|
-
|
22
|
-
func testThatUpdatingExternalStylesWithAnInvalidPathProducesAValidStylesheet() {
|
23
|
-
var stylesheet = Stylesheet(color: "black", fontSize: 10)
|
24
|
-
stylesheet.updateWith(filePath: "/this-is-a-fake-path-that-should-never-exist")
|
25
|
-
|
26
|
-
debugPrint(stylesheet.contents, getTestCase(named: "default-stylesheet"))
|
27
|
-
|
28
|
-
|
29
|
-
XCTAssert(stylesheet.contents == getTestCase(named: "default-stylesheet"))
|
30
|
-
}
|
31
|
-
}
|