Skip to content

Commit 30942e7

Browse files
committed
Added support for Homesite palette files (hpl)
1 parent 64aedc8 commit 30942e7

File tree

11 files changed

+754
-11
lines changed

11 files changed

+754
-11
lines changed

Quicklook/Palette Viewer/Palette Viewer.xcodeproj/project.pbxproj

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@
770770
"@executable_path/../../../../Frameworks",
771771
);
772772
MACOSX_DEPLOYMENT_TARGET = 13.0;
773-
MARKETING_VERSION = 2.0.7;
773+
MARKETING_VERSION = 2.0.8;
774774
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.gradient-thumbnail-generator";
775775
PRODUCT_NAME = "$(TARGET_NAME)";
776776
SKIP_INSTALL = YES;
@@ -800,7 +800,7 @@
800800
"@executable_path/../../../../Frameworks",
801801
);
802802
MACOSX_DEPLOYMENT_TARGET = 13.0;
803-
MARKETING_VERSION = 2.0.7;
803+
MARKETING_VERSION = 2.0.8;
804804
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.gradient-thumbnail-generator";
805805
PRODUCT_NAME = "$(TARGET_NAME)";
806806
SKIP_INSTALL = YES;
@@ -947,7 +947,7 @@
947947
"@executable_path/../../../../Frameworks",
948948
);
949949
MACOSX_DEPLOYMENT_TARGET = 13.0;
950-
MARKETING_VERSION = 2.0.7;
950+
MARKETING_VERSION = 2.0.8;
951951
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.quicklook-palette";
952952
PRODUCT_NAME = "$(TARGET_NAME)";
953953
SKIP_INSTALL = YES;
@@ -977,7 +977,7 @@
977977
"@executable_path/../../../../Frameworks",
978978
);
979979
MACOSX_DEPLOYMENT_TARGET = 13.0;
980-
MARKETING_VERSION = 2.0.7;
980+
MARKETING_VERSION = 2.0.8;
981981
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.quicklook-palette";
982982
PRODUCT_NAME = "$(TARGET_NAME)";
983983
SKIP_INSTALL = YES;
@@ -1012,7 +1012,7 @@
10121012
"@executable_path/../Frameworks",
10131013
);
10141014
MACOSX_DEPLOYMENT_TARGET = 14.0;
1015-
MARKETING_VERSION = 2.0.7;
1015+
MARKETING_VERSION = 2.0.8;
10161016
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer";
10171017
PRODUCT_NAME = "$(TARGET_NAME)";
10181018
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -1046,7 +1046,7 @@
10461046
"@executable_path/../Frameworks",
10471047
);
10481048
MACOSX_DEPLOYMENT_TARGET = 14.0;
1049-
MARKETING_VERSION = 2.0.7;
1049+
MARKETING_VERSION = 2.0.8;
10501050
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer";
10511051
PRODUCT_NAME = "$(TARGET_NAME)";
10521052
SWIFT_EMIT_LOC_STRINGS = YES;
@@ -1075,7 +1075,7 @@
10751075
"@executable_path/../../../../Frameworks",
10761076
);
10771077
MACOSX_DEPLOYMENT_TARGET = 13.0;
1078-
MARKETING_VERSION = 2.0.7;
1078+
MARKETING_VERSION = 2.0.8;
10791079
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.palette-thumbnail-generator";
10801080
PRODUCT_NAME = "$(TARGET_NAME)";
10811081
SKIP_INSTALL = YES;
@@ -1105,7 +1105,7 @@
11051105
"@executable_path/../../../../Frameworks",
11061106
);
11071107
MACOSX_DEPLOYMENT_TARGET = 13.0;
1108-
MARKETING_VERSION = 2.0.7;
1108+
MARKETING_VERSION = 2.0.8;
11091109
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.palette-thumbnail-generator";
11101110
PRODUCT_NAME = "$(TARGET_NAME)";
11111111
SKIP_INSTALL = YES;
@@ -1136,7 +1136,7 @@
11361136
"@executable_path/../../../../Frameworks",
11371137
);
11381138
MACOSX_DEPLOYMENT_TARGET = 13.0;
1139-
MARKETING_VERSION = 2.0.7;
1139+
MARKETING_VERSION = 2.0.8;
11401140
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.quicklook-gradient";
11411141
PRODUCT_NAME = "$(TARGET_NAME)";
11421142
SKIP_INSTALL = YES;
@@ -1167,7 +1167,7 @@
11671167
"@executable_path/../../../../Frameworks",
11681168
);
11691169
MACOSX_DEPLOYMENT_TARGET = 13.0;
1170-
MARKETING_VERSION = 2.0.7;
1170+
MARKETING_VERSION = 2.0.8;
11711171
PRODUCT_BUNDLE_IDENTIFIER = "org.dagronf.palette-viewer.quicklook-gradient";
11721172
PRODUCT_NAME = "$(TARGET_NAME)";
11731173
SKIP_INSTALL = YES;

Quicklook/Palette Viewer/Palette Viewer/Info.plist

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,20 @@
558558
<key>NSDocumentClass</key>
559559
<string>$(PRODUCT_MODULE_NAME).Document</string>
560560
</dict>
561+
<dict>
562+
<key>CFBundleTypeName</key>
563+
<string>Homesite Palette</string>
564+
<key>CFBundleTypeRole</key>
565+
<string>Editor</string>
566+
<key>LSHandlerRank</key>
567+
<string>Default</string>
568+
<key>LSItemContentTypes</key>
569+
<array>
570+
<string>public.dagronf.colorpalette.palette.homesite</string>
571+
</array>
572+
<key>NSDocumentClass</key>
573+
<string>$(PRODUCT_MODULE_NAME).Document</string>
574+
</dict>
561575
</array>
562576
<key>UTExportedTypeDeclarations</key>
563577
<array>
@@ -1230,6 +1244,25 @@
12301244
</array>
12311245
</dict>
12321246
</dict>
1247+
<dict>
1248+
<key>UTTypeConformsTo</key>
1249+
<array>
1250+
<string>public.text</string>
1251+
</array>
1252+
<key>UTTypeDescription</key>
1253+
<string>Homesite Palette</string>
1254+
<key>UTTypeIcons</key>
1255+
<dict/>
1256+
<key>UTTypeIdentifier</key>
1257+
<string>public.dagronf.colorpalette.palette.homesite</string>
1258+
<key>UTTypeTagSpecification</key>
1259+
<dict>
1260+
<key>public.filename-extension</key>
1261+
<array>
1262+
<string>hpl</string>
1263+
</array>
1264+
</dict>
1265+
</dict>
12331266
</array>
12341267
<key>UTImportedTypeDeclarations</key>
12351268
<array>

Quicklook/Palette Viewer/PaletteViewerPaletteThumbnailGenerator/Info.plist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
<string>public.dagronf.colorpalette.palette.xara.palette</string>
3838
<string>public.dagronf.colorpalette.palette.sk1</string>
3939
<string>public.dagronf.colorpalette.palette.koffice</string>
40+
<string>public.dagronf.colorpalette.palette.homesite</string>
4041
</array>
4142
<key>QLThumbnailMinimumDimension</key>
4243
<integer>0</integer>

Quicklook/Palette Viewer/PaletteViewerQuickLookPlugin/Info.plist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<string>public.dagronf.colorpalette.palette.xara.palette</string>
4040
<string>public.dagronf.colorpalette.palette.sk1</string>
4141
<string>public.dagronf.colorpalette.palette.koffice</string>
42+
<string>public.dagronf.colorpalette.palette.homesite</string>
4243
</array>
4344
<key>QLSupportsSearchableItems</key>
4445
<false/>

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Supports the following :-
5252
* Xara Designer Palette format (`.jcw`)
5353
* SK1 Color Palette (`.skp`) ***Very basic support***
5454
* KOffice Color Palette (`.colors`)
55+
* Homesite Palette (`.hpl`)
5556

5657
## Supported gradient formats
5758

@@ -114,6 +115,7 @@ Some features :-
114115
|`PAL.Coder.DCP` | Built-in binary palette format (.dcp) |
115116
|`PAL.Coder.GIMP` | GIMP palette files (.gpl) |
116117
|`PAL.Coder.HEX` | Hex Color Palette (`.hex`) |
118+
|`PAL.Coder.HPL` | Homesite Palette (`.hpl`) |
117119
|`PAL.Coder.Image` | Image files (.png, .jpg, .gif) |
118120
|`PAL.Coder.JSON` | JSON encoded palette (.jsoncolorpalette) |
119121
|`PAL.Coder.OpenOfficePaletteCoder` | OpenOffice Palette (.soc) |
@@ -212,6 +214,7 @@ let paletteData = palette.export(format: .ase)
212214
| `PAL.Coder.DCP` | Binary |||||||||
213215
| `PAL.Coder.GIMP` | Text ||||||| RGB only ||
214216
| `PAL.Coder.HEX` | Text ||||||| RGB only ||
217+
| `PAL.Coder.HPL` | Text ||||||| RGB only ||
215218
| `PAL.Coder.Image` | Binary |||||||||
216219
| `PAL.Coder.JSON` | JSON Text |||||||||
217220
| `PAL.Coder.KOffice` | Text ||||||| RGB only ||
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
//
2+
// Copyright © 2025 Darren Ford. All rights reserved.
3+
//
4+
// MIT license
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7+
// documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
8+
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9+
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included in all copies or substantial
12+
// portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
15+
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
16+
// OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
17+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18+
//
19+
20+
import DSFRegex
21+
import Foundation
22+
23+
public extension PAL.Coder {
24+
/// Homesite Palette Decoder/Encoder
25+
/// Allaire Homesite, Macromedia ColdFusion
26+
struct HPL: PAL_PaletteCoder {
27+
public let format: PAL.PaletteFormat = .hpl
28+
public let name = "Homesite Palette"
29+
public static var utTypeString: String = "public.dagronf.colorpalette.palette.homesite"
30+
public let fileExtension = ["hpl"]
31+
}
32+
}
33+
34+
public extension PAL.Coder.HPL {
35+
func decode(from inputStream: InputStream) throws -> PAL.Palette {
36+
// Load a string from the input stream
37+
guard let decoded = String.decode(from: inputStream) else {
38+
ColorPaletteLogger.log(.error, "HPLCoder: Unexpected text encoding")
39+
throw PAL.CommonError.invalidFormat
40+
}
41+
let content = decoded.text
42+
43+
let lines = content.lines
44+
guard
45+
lines.count > 0,
46+
lines[0].contains("Palette"),
47+
lines[1].contains("Version 4.0")
48+
else {
49+
throw PAL.CommonError.invalidFormat
50+
}
51+
52+
var palette = PAL.Palette(format: self.format)
53+
let colorRegex = try DSFRegex(#"^\s*(\d+)\s+(\d+)\s+(\d+).*$"#)
54+
55+
for line in lines.dropFirst(3) {
56+
let lineStr = String(line)
57+
58+
if let match = colorRegex.matches(for: lineStr).matches.first {
59+
let rs = lineStr[match.captures[0]]
60+
let gs = lineStr[match.captures[1]]
61+
let bs = lineStr[match.captures[2]]
62+
63+
guard
64+
let rv = Int(rs),
65+
let gv = Int(gs),
66+
let bv = Int(bs)
67+
else {
68+
continue
69+
}
70+
71+
let re = max(0, min(1, Double(rv) / 255.0))
72+
let ge = max(0, min(1, Double(gv) / 255.0))
73+
let be = max(0, min(1, Double(bv) / 255.0))
74+
75+
let c = try PAL.Color(colorSpace: .RGB, colorComponents: [re, ge, be])
76+
palette.colors.append(c)
77+
}
78+
}
79+
return palette
80+
}
81+
}
82+
83+
public extension PAL.Coder.HPL {
84+
func encode(_ palette: PAL.Palette) throws -> Data {
85+
// Write the header info
86+
var result = "Palette\nVersion 4.0\n-----------\n"
87+
88+
// Flatten _all_ the colors in the palette (including global and group colors) to an RGB color list
89+
let flattenedColors = try palette.allColors().map { try $0.converted(to: .RGB) }
90+
91+
for rgb in flattenedColors {
92+
let c = try rgb.rgb()
93+
result += "\(c.r255) \(c.g255) \(c.b255)\n"
94+
}
95+
guard let data = result.data(using: .utf8) else {
96+
throw PAL.CommonError.unsupportedColorSpace
97+
}
98+
99+
return data
100+
}
101+
}
102+
103+
// MARK: - UTType identifiers
104+
105+
#if canImport(UniformTypeIdentifiers)
106+
import UniformTypeIdentifiers
107+
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
108+
public extension UTType {
109+
static let homesitePalette = UTType(PAL.Coder.HPL.utTypeString)!
110+
}
111+
#endif

Sources/ColorPaletteCodable/palette/PAL+PaletteFormat.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public extension PAL {
6060
case afpalette // Affinity Designer .afpalette file
6161
case xara // Xara palette file (.jcw)
6262
case koffice // KOffice palette file (.colors)
63+
case hpl // Homesite Palette file (.hpl)
6364

6465
// This needs to go last, so it doesn't override the other PAL types
6566
case vga24bit // 24-bit RGB VGA (3 bytes RRGGBB)
@@ -111,6 +112,7 @@ public extension PAL.PaletteFormat {
111112
case .afpalette : return PAL.Coder.AFPaletteCoder()
112113
case .xara : return PAL.Coder.JCW()
113114
case .koffice : return PAL.Coder.KOffice()
115+
case .hpl : return PAL.Coder.HPL()
114116
}
115117
}
116118
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
@testable import ColorPaletteCodable
2+
import XCTest
3+
import Foundation
4+
5+
class HPLPaletteTests: XCTestCase {
6+
7+
func testLoad1() throws {
8+
let palette = try loadResourcePalette(named: "hpl1_v4.0.hpl")
9+
let colors = palette.allColors()
10+
XCTAssertEqual(colors.count, 287)
11+
}
12+
13+
func testLoad2() throws {
14+
let palette = try loadResourcePalette(named: "hpl2_v4.0.hpl")
15+
let colors = palette.allColors()
16+
XCTAssertEqual(colors.count, 256)
17+
}
18+
19+
func testBasicSaveReload() throws {
20+
let paletteURL = try XCTUnwrap(Bundle.module.url(forResource: "Default", withExtension: "gpl"))
21+
let palette = try PAL.Palette.Decode(from: paletteURL)
22+
XCTAssertEqual(23, palette.colors.count)
23+
24+
let saved = try PAL.Coder.HPL().encode(palette)
25+
26+
let loaded = try PAL.Coder.HPL().decode(from: saved)
27+
XCTAssertEqual(23, loaded.colors.count)
28+
29+
// HPL doesn't save the color names
30+
for color in loaded.colors {
31+
XCTAssertEqual("", color.name)
32+
}
33+
34+
XCTAssertEqual(loaded.colors[11].colorComponents[0], 0.509803921, accuracy: 0.0001)
35+
XCTAssertEqual(loaded.colors[11].colorComponents[1], 0.498039215, accuracy: 0.0001)
36+
XCTAssertEqual(loaded.colors[11].colorComponents[2], 0.0, accuracy: 0.01)
37+
38+
XCTAssertEqual(loaded.colors[13].colorComponents[0], 0.1, accuracy: 0.01)
39+
XCTAssertEqual(loaded.colors[13].colorComponents[1], 0.1, accuracy: 0.01)
40+
XCTAssertEqual(loaded.colors[13].colorComponents[2], 0.1, accuracy: 0.01)
41+
}
42+
}

Tests/ColorPaletteCodableTests/palette/KOfficePaletteTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
@testable import ColorPaletteCodable
22
import XCTest
3-
43
import Foundation
54

65
class KOfficePaletteTests: XCTestCase {
@@ -36,3 +35,5 @@ class KOfficePaletteTests: XCTestCase {
3635
XCTAssertEqual(loaded.colors[13].colorComponents[2], 0.1, accuracy: 0.01)
3736
}
3837
}
38+
39+

0 commit comments

Comments
 (0)