Support .lottie files in LottieGen. (#376)
* Support .lottie files in LottieGen. See https://dotlottie.io for file details. LottieGen will now read .lottie files as well as .json files.
This commit is contained in:
Родитель
67c626d0d3
Коммит
0589774252
|
@ -0,0 +1,261 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using Microsoft.Toolkit.Uwp.UI.Lottie.DotLottie;
|
||||
|
||||
/// <summary>
|
||||
/// Processes a file containing Lottie animations and produces various generated outputs.
|
||||
/// Both JSON and .lottie files are supported.
|
||||
/// </summary>
|
||||
sealed class FileProcessor
|
||||
{
|
||||
readonly CommandLineOptions _options;
|
||||
readonly Reporter _reporter;
|
||||
readonly string _filePath;
|
||||
readonly string _outputFolder;
|
||||
readonly DateTime _timestamp;
|
||||
|
||||
FileProcessor(
|
||||
CommandLineOptions options,
|
||||
Reporter reporter,
|
||||
string filePath,
|
||||
string outputFolder,
|
||||
DateTime timestamp)
|
||||
{
|
||||
_options = options;
|
||||
_filePath = filePath;
|
||||
_reporter = reporter;
|
||||
_outputFolder = outputFolder;
|
||||
_timestamp = timestamp;
|
||||
}
|
||||
|
||||
internal static bool ProcessFile(
|
||||
CommandLineOptions options,
|
||||
Reporter reporter,
|
||||
string filePath,
|
||||
string outputFolder,
|
||||
DateTime timestamp)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new FileProcessor(options, reporter, filePath, outputFolder, timestamp).Run();
|
||||
}
|
||||
catch
|
||||
{
|
||||
reporter.WriteError($"Unhandled exception processing: {filePath}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
bool Run()
|
||||
{
|
||||
switch (Path.GetExtension(_filePath).ToLowerInvariant())
|
||||
{
|
||||
case ".lottie":
|
||||
return ProcessDotLottieFile(
|
||||
options: _options,
|
||||
reporter: _reporter,
|
||||
filePath: _filePath,
|
||||
outputFolder: _outputFolder,
|
||||
timestamp: _timestamp);
|
||||
|
||||
case ".json":
|
||||
default:
|
||||
return ProcessJsonLottieFile(
|
||||
options: _options,
|
||||
reporter: _reporter,
|
||||
jsonFilePath: _filePath,
|
||||
outputFolder: _outputFolder,
|
||||
timestamp: _timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
static bool ProcessJsonLottieFile(
|
||||
CommandLineOptions options,
|
||||
Reporter reporter,
|
||||
string jsonFilePath,
|
||||
string outputFolder,
|
||||
DateTime timestamp)
|
||||
{
|
||||
using var jsonStream = TryOpenFile(jsonFilePath, reporter);
|
||||
|
||||
if (jsonStream is null)
|
||||
{
|
||||
reporter.WriteError($"Failed to read {jsonFilePath}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return LottieJsonFileProcessor.ProcessLottieJsonFile(
|
||||
options: options,
|
||||
reporter: reporter,
|
||||
sourceFilePath: jsonFilePath,
|
||||
jsonFilePath: jsonFilePath,
|
||||
className: GetClassName(jsonFilePath),
|
||||
jsonStream: jsonStream,
|
||||
outputFolder: outputFolder,
|
||||
timestamp: timestamp);
|
||||
}
|
||||
|
||||
static bool ProcessDotLottieFile(
|
||||
CommandLineOptions options,
|
||||
Reporter reporter,
|
||||
string filePath,
|
||||
string outputFolder,
|
||||
DateTime timestamp)
|
||||
{
|
||||
using var fileStream = TryOpenFile(filePath, reporter);
|
||||
|
||||
if (fileStream is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ZipArchive zipArchive;
|
||||
try
|
||||
{
|
||||
zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read);
|
||||
}
|
||||
catch (InvalidDataException e)
|
||||
{
|
||||
using (reporter.ErrorStream.Lock())
|
||||
{
|
||||
reporter.WriteError($"File {filePath} is not a valid .lottie file.");
|
||||
reporter.WriteError(e.Message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var dotLottieFile = DotLottieFile.FromZipArchive(zipArchive);
|
||||
|
||||
if (dotLottieFile is null)
|
||||
{
|
||||
reporter.WriteError($"File {filePath} is not a valid .lottie file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dotLottieFile.Animations.Count == 0)
|
||||
{
|
||||
reporter.WriteError($"File {filePath} contains no animations.");
|
||||
return false;
|
||||
}
|
||||
|
||||
var succeeded = true;
|
||||
foreach (var animation in dotLottieFile.Animations)
|
||||
{
|
||||
var jsonPath = $"{filePath}{animation.Path}";
|
||||
|
||||
using var jsonStream = animation.Open();
|
||||
|
||||
if (jsonStream == null)
|
||||
{
|
||||
reporter.WriteError($"Failed to read from {jsonPath}.");
|
||||
succeeded = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The name of the class should reflect the name of the .lottie file,
|
||||
// but if there are multiple .json files in the .lottie file then we
|
||||
// need to differentiate them by adding the file name.
|
||||
var className = dotLottieFile.Animations.Count == 1
|
||||
? GetClassName(filePath)
|
||||
: GetClassName($"{filePath}_{animation.Id}");
|
||||
|
||||
if (!LottieJsonFileProcessor.ProcessLottieJsonFile(
|
||||
options: options,
|
||||
reporter: reporter,
|
||||
sourceFilePath: filePath,
|
||||
jsonFilePath: jsonPath,
|
||||
className: className,
|
||||
jsonStream: jsonStream,
|
||||
outputFolder: outputFolder,
|
||||
timestamp))
|
||||
{
|
||||
succeeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
static Stream? TryOpenFile(string filePath, Reporter reporter)
|
||||
{
|
||||
using (reporter.InfoStream.Lock())
|
||||
{
|
||||
reporter.WriteInfo($"Reading file:");
|
||||
reporter.WriteInfo(InfoType.FilePath, $" {filePath}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return File.OpenRead(filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
using (reporter.ErrorStream.Lock())
|
||||
{
|
||||
reporter.WriteError($"Failed to read from {filePath}");
|
||||
reporter.WriteError(e.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get an appropriate name for a generated class.
|
||||
static string GetClassName(string path) =>
|
||||
TrySynthesizeClassName(Path.GetFileNameWithoutExtension(path)) ??
|
||||
"Lottie"; // If all else fails, just call it Lottie.
|
||||
|
||||
/// <summary>
|
||||
/// Takes a name and modifies it as necessary to be suited for use as a class name in languages such
|
||||
/// as C# and C++.
|
||||
/// Returns null on failure.
|
||||
/// </summary>
|
||||
/// <returns>A name, or null.</returns>
|
||||
static string? TrySynthesizeClassName(string? name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove any leading punctuation.
|
||||
var prefixSize = name.TakeWhile(c => !char.IsLetterOrDigit(c)).Count();
|
||||
|
||||
return SanitizeTypeName(name.Substring(prefixSize));
|
||||
}
|
||||
|
||||
// Makes the given name suitable for use as a class name in languages such as C# and C++.
|
||||
static string? SanitizeTypeName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the first character is not a letter, prepend an underscore.
|
||||
if (!char.IsLetter(name, 0))
|
||||
{
|
||||
name = "_" + name;
|
||||
}
|
||||
|
||||
// Replace any disallowed character with underscores.
|
||||
name =
|
||||
new string((from ch in name
|
||||
select char.IsLetterOrDigit(ch) ? ch : '_').ToArray());
|
||||
|
||||
// Remove any duplicated underscores.
|
||||
name = name.Replace("__", "_");
|
||||
|
||||
// Capitalize the first letter.
|
||||
name = name.ToUpperInvariant().Substring(0, 1) + name.Substring(1);
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -16,8 +16,6 @@
|
|||
<AssemblyName>LottieGen</AssemblyName>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<!-- Disable warnings about XML comments temporarily until all the comments have been authored. -->
|
||||
<NoWarn>1572;1573;1574;1591</NoWarn>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<IsPackable>True</IsPackable>
|
||||
<StartupObject>Program</StartupObject>
|
||||
|
@ -28,6 +26,9 @@
|
|||
<PackageReference Include="System.Numerics.Vectors" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\source\CompMetadata\CompMetadata.projitems" Label="Shared" />
|
||||
<Import Project="..\source\DotLottie\DotLottie.projitems" Label="Shared" />
|
||||
<Import Project="..\source\GenericData\GenericData.projitems" Label="Shared" />
|
||||
<Import Project="..\source\LottieData\LottieData.projitems" Label="Shared" />
|
||||
<Import Project="..\source\LottieMetadata\LottieMetadata.projitems" Label="Shared" />
|
||||
<Import Project="..\source\LottieReader\LottieReader.projitems" Label="Shared" />
|
||||
|
@ -37,8 +38,6 @@
|
|||
<Import Project="..\source\WinStorageStreamsData\WinStorageStreamsData.projitems" Label="Shared" />
|
||||
<Import Project="..\source\WinUIXamlMediaData\WinUIXamlMediaData.projitems" Label="Shared" />
|
||||
<Import Project="..\source\YamlData\YamlData.projitems" Label="Shared" />
|
||||
<Import Project="..\source\GenericData\GenericData.projitems" Label="Shared" />
|
||||
<Import Project="..\source\UIDataCodeGen\UIDataCodeGen.projitems" Label="Shared" />
|
||||
<Import Project="..\source\CompMetadata\CompMetadata.projitems" Label="Shared" />
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -94,6 +94,8 @@ Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "LottieMetadata", "..\source
|
|||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "CompMetadata", "..\source\CompMetadata\CompMetadata.shproj", "{B0197C19-BDF5-473E-A022-E21F6122EEE5}"
|
||||
EndProject
|
||||
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "DotLottie", "..\source\DotLottie\DotLottie.shproj", "{7012420D-624C-4BD4-A1D2-1C6C1655ED3A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SharedMSBuildProjectFiles) = preSolution
|
||||
..\source\LottieToWinComp\LottieToWinComp.projitems*{0340244a-683c-405e-838b-f93872779532}*SharedItemsImports = 13
|
||||
|
@ -102,11 +104,13 @@ Global
|
|||
..\source\WinUIXamlMediaData\WinUIXamlMediaData.projitems*{30059ca7-0745-4eec-8d11-b14850a70c98}*SharedItemsImports = 13
|
||||
..\source\YamlData\YamlData.projitems*{39c6b7f3-5e75-4019-82ab-00fd8a0a06e2}*SharedItemsImports = 13
|
||||
..\source\LottieReader\LottieReader.projitems*{4e7d8957-3f5f-46e1-99a8-2012b806c9b0}*SharedItemsImports = 13
|
||||
..\source\DotLottie\DotLottie.projitems*{7012420d-624c-4bd4-a1d2-1c6c1655ed3a}*SharedItemsImports = 13
|
||||
..\source\UIData\UIData.projitems*{74601e6c-2dfe-4842-b170-047941abff2c}*SharedItemsImports = 13
|
||||
..\source\GenericData\GenericData.projitems*{77bcd724-8555-463b-985f-f8e8110164c4}*SharedItemsImports = 13
|
||||
..\source\CompMetadata\CompMetadata.projitems*{b0197c19-bdf5-473e-a022-e21f6122eee5}*SharedItemsImports = 13
|
||||
..\source\LottieData\LottieData.projitems*{b3db16ee-a821-4474-a188-e64926529bbd}*SharedItemsImports = 13
|
||||
..\source\CompMetadata\CompMetadata.projitems*{cb12d5ba-a6fe-41e2-b555-83c903cce92a}*SharedItemsImports = 5
|
||||
..\source\DotLottie\DotLottie.projitems*{cb12d5ba-a6fe-41e2-b555-83c903cce92a}*SharedItemsImports = 5
|
||||
..\source\GenericData\GenericData.projitems*{cb12d5ba-a6fe-41e2-b555-83c903cce92a}*SharedItemsImports = 5
|
||||
..\source\LottieData\LottieData.projitems*{cb12d5ba-a6fe-41e2-b555-83c903cce92a}*SharedItemsImports = 5
|
||||
..\source\LottieMetadata\LottieMetadata.projitems*{cb12d5ba-a6fe-41e2-b555-83c903cce92a}*SharedItemsImports = 5
|
||||
|
@ -165,6 +169,7 @@ Global
|
|||
{D02BE6C8-14DB-4B4F-8600-F3C9B69C104D} = {AB232F35-AAF7-4AE2-B1D2-45DD9BC2F7D7}
|
||||
{04B43A1A-DDFB-4A61-BF36-39F5E666C702} = {AB232F35-AAF7-4AE2-B1D2-45DD9BC2F7D7}
|
||||
{B0197C19-BDF5-473E-A022-E21F6122EEE5} = {AB232F35-AAF7-4AE2-B1D2-45DD9BC2F7D7}
|
||||
{7012420D-624C-4BD4-A1D2-1C6C1655ED3A} = {AB232F35-AAF7-4AE2-B1D2-45DD9BC2F7D7}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {51B9BB4C-5196-41CF-950C-12B04AD8A61C}
|
||||
|
|
|
@ -18,14 +18,15 @@ using Microsoft.Toolkit.Uwp.UI.Lottie.UIData.Tools;
|
|||
using Microsoft.Toolkit.Uwp.UI.Lottie.WinCompData;
|
||||
|
||||
/// <summary>
|
||||
/// Processes a single Lottie file to produce various generated outputs.
|
||||
/// Processes a single Lottie .json file to produce various generated outputs.
|
||||
/// </summary>
|
||||
sealed class LottieFileProcessor
|
||||
sealed class LottieJsonFileProcessor
|
||||
{
|
||||
readonly CommandLineOptions _options;
|
||||
readonly Profiler _profiler = new Profiler();
|
||||
readonly Reporter _reporter;
|
||||
readonly string _lottieFilePath;
|
||||
readonly string _sourceFilePath;
|
||||
readonly string _jsonFilePath;
|
||||
readonly string _outputFolder;
|
||||
readonly DateTime _timestamp;
|
||||
readonly uint _minimumUapVersion;
|
||||
|
@ -39,16 +40,20 @@ sealed class LottieFileProcessor
|
|||
IReadOnlyList<TranslationResult>? _translationResults;
|
||||
IReadOnlyList<(TranslationIssue issue, UapVersionRange versionRange)>? _translationIssues;
|
||||
|
||||
LottieFileProcessor(
|
||||
LottieJsonFileProcessor(
|
||||
CommandLineOptions options,
|
||||
Reporter reporter,
|
||||
string lottieFilePath,
|
||||
string sourceFilePath,
|
||||
string jsonFilePath,
|
||||
string className,
|
||||
string outputFolder,
|
||||
DateTime timestamp)
|
||||
{
|
||||
_options = options;
|
||||
_reporter = reporter;
|
||||
_lottieFilePath = lottieFilePath;
|
||||
_sourceFilePath = sourceFilePath;
|
||||
_jsonFilePath = jsonFilePath;
|
||||
_className = className;
|
||||
_outputFolder = outputFolder;
|
||||
_timestamp = timestamp;
|
||||
|
||||
|
@ -56,32 +61,39 @@ sealed class LottieFileProcessor
|
|||
const uint defaultUapVersion = 7;
|
||||
|
||||
_minimumUapVersion = _options.MinimumUapVersion ?? defaultUapVersion;
|
||||
|
||||
// Get an appropriate name for a generated class.
|
||||
_className =
|
||||
InstantiatorGeneratorBase.TrySynthesizeClassName(System.IO.Path.GetFileNameWithoutExtension(_lottieFilePath)) ??
|
||||
"Lottie"; // If all else fails, just call it Lottie.
|
||||
}
|
||||
|
||||
internal static bool ProcessLottieFile(
|
||||
/// <summary>
|
||||
/// Processes a single .json file containing a Lottie animation.
|
||||
/// </summary>
|
||||
/// <param name="options">The kinds of processing to perform.</param>
|
||||
/// <param name="reporter">Receives progress and status information.</param>
|
||||
/// <param name="sourceFilePath">The file in which the Lottie animation is contained.</param>
|
||||
/// <param name="jsonFilePath">A path describing where the .json file is.</param>
|
||||
/// <param name="className">The name to give to the generated C# and/or C++ class.</param>
|
||||
/// <param name="jsonStream">The stream of bytes containined in the .json file.</param>
|
||||
/// <param name="outputFolder">Where the output should be written.</param>
|
||||
/// <param name="timestamp">A timestamp used to indicate when the processing took place.</param>
|
||||
/// <returns><c>true</c> if the processing succeeded.</returns>
|
||||
internal static bool ProcessLottieJsonFile(
|
||||
CommandLineOptions options,
|
||||
Reporter reporter,
|
||||
string lottieFilePath,
|
||||
string sourceFilePath,
|
||||
string jsonFilePath,
|
||||
string className,
|
||||
Stream jsonStream,
|
||||
string outputFolder,
|
||||
DateTime timestamp)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new LottieFileProcessor(options, reporter, lottieFilePath, outputFolder, timestamp).Run();
|
||||
}
|
||||
catch
|
||||
{
|
||||
reporter.WriteError($"Unhandled exception processing: {lottieFilePath}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
DateTime timestamp) =>
|
||||
new LottieJsonFileProcessor(
|
||||
options: options,
|
||||
reporter: reporter,
|
||||
sourceFilePath: sourceFilePath,
|
||||
jsonFilePath: jsonFilePath,
|
||||
className: className,
|
||||
outputFolder: outputFolder,
|
||||
timestamp: timestamp).Run(jsonStream);
|
||||
|
||||
bool Run()
|
||||
bool Run(Stream jsonStream)
|
||||
{
|
||||
// Make sure we can write to the output directory.
|
||||
if (!TryEnsureDirectoryExists(_outputFolder))
|
||||
|
@ -90,14 +102,6 @@ sealed class LottieFileProcessor
|
|||
return false;
|
||||
}
|
||||
|
||||
// Read the Lottie .json text.
|
||||
var jsonStream = TryReadTextFile(_lottieFilePath);
|
||||
|
||||
if (jsonStream is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse the Lottie.
|
||||
var lottieComposition =
|
||||
LottieCompositionReader.ReadLottieCompositionFromJsonStream(
|
||||
|
@ -109,12 +113,12 @@ sealed class LottieFileProcessor
|
|||
|
||||
foreach (var issue in _readerIssues)
|
||||
{
|
||||
_reporter.WriteInfo(InfoType.Issue, IssueToString(_lottieFilePath, issue));
|
||||
_reporter.WriteInfo(InfoType.Issue, IssueToString(_jsonFilePath, issue));
|
||||
}
|
||||
|
||||
if (lottieComposition is null)
|
||||
{
|
||||
_reporter.WriteError($"Failed to parse Lottie file: {_lottieFilePath}");
|
||||
_reporter.WriteError($"Failed to parse Lottie file: {_jsonFilePath}");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -130,9 +134,7 @@ sealed class LottieFileProcessor
|
|||
|
||||
bool TryGenerateCode(LottieComposition lottieComposition)
|
||||
{
|
||||
var lottieFileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(_lottieFilePath);
|
||||
|
||||
var outputFileBase = System.IO.Path.Combine(_outputFolder, lottieFileNameWithoutExtension);
|
||||
var outputFileBase = System.IO.Path.Combine(_outputFolder, _className);
|
||||
|
||||
var codeGenSucceeded = true;
|
||||
|
||||
|
@ -211,7 +213,7 @@ sealed class LottieFileProcessor
|
|||
"Timing",
|
||||
new[]
|
||||
{
|
||||
("Path", _lottieFilePath),
|
||||
("Path", _jsonFilePath),
|
||||
("TotalSeconds", (_profiler.ParseTime +
|
||||
_profiler.TranslateTime +
|
||||
_profiler.OptimizationTime +
|
||||
|
@ -234,7 +236,7 @@ sealed class LottieFileProcessor
|
|||
"Lottie",
|
||||
new[]
|
||||
{
|
||||
("Path", _lottieFilePath),
|
||||
("Path", _jsonFilePath),
|
||||
("Name", _lottieStats.Name),
|
||||
("BodyMovinVersion", _lottieStats.Version.ToString()),
|
||||
("DurationSeconds", _lottieStats.Duration.TotalSeconds.ToString()),
|
||||
|
@ -266,7 +268,7 @@ sealed class LottieFileProcessor
|
|||
"WinComp",
|
||||
new[]
|
||||
{
|
||||
("Path", _lottieFilePath),
|
||||
("Path", _jsonFilePath),
|
||||
("Animator", translationStats.AnimatorCount.ToString()),
|
||||
("BooleanKeyFrameAnimation", translationStats.BooleanKeyFrameAnimationCount.ToString()),
|
||||
("CanvasGeometry", translationStats.CanvasGeometryCount.ToString()),
|
||||
|
@ -316,7 +318,7 @@ sealed class LottieFileProcessor
|
|||
"WinCompOptimization",
|
||||
new[]
|
||||
{
|
||||
("Path", _lottieFilePath),
|
||||
("Path", _jsonFilePath),
|
||||
("Animator", (_afterOptimizationStats.AnimatorCount - _beforeOptimizationStats.AnimatorCount).ToString()),
|
||||
("BooleanKeyFrameAnimation", (_afterOptimizationStats.BooleanKeyFrameAnimationCount - _beforeOptimizationStats.BooleanKeyFrameAnimationCount).ToString()),
|
||||
("CanvasGeometry", (_afterOptimizationStats.CanvasGeometryCount - _beforeOptimizationStats.CanvasGeometryCount).ToString()),
|
||||
|
@ -364,7 +366,7 @@ sealed class LottieFileProcessor
|
|||
"Errors",
|
||||
new[]
|
||||
{
|
||||
("Path", _lottieFilePath),
|
||||
("Path", _jsonFilePath),
|
||||
("ErrorCode", code),
|
||||
("Description", description),
|
||||
});
|
||||
|
@ -380,7 +382,7 @@ sealed class LottieFileProcessor
|
|||
writer => LottieCompositionYamlSerializer.WriteYaml(
|
||||
lottieComposition,
|
||||
writer,
|
||||
System.IO.Path.GetFileName(_lottieFilePath)));
|
||||
comment: System.IO.Path.GetFileName(_jsonFilePath)));
|
||||
|
||||
if (result)
|
||||
{
|
||||
|
@ -624,30 +626,6 @@ sealed class LottieFileProcessor
|
|||
return true;
|
||||
}
|
||||
|
||||
Stream? TryReadTextFile(string filePath)
|
||||
{
|
||||
using (_reporter.InfoStream.Lock())
|
||||
{
|
||||
_reporter.WriteInfo($"Reading file:");
|
||||
_reporter.WriteInfo(InfoType.FilePath, $" {_lottieFilePath}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return File.OpenRead(filePath);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
using (_reporter.ErrorStream.Lock())
|
||||
{
|
||||
_reporter.WriteError($"Failed to read from {filePath}");
|
||||
_reporter.WriteError(e.Message);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
CodegenConfiguration CreateCodeGenConfiguration(
|
||||
LottieComposition lottieComposition,
|
||||
string languageSwitch)
|
||||
|
@ -692,7 +670,7 @@ sealed class LottieFileProcessor
|
|||
// be included in the generated output.
|
||||
IEnumerable<string> GetToolInvocationInfo(string languageSwitch)
|
||||
{
|
||||
var inputFile = new FileInfo(_lottieFilePath);
|
||||
var inputFile = new FileInfo(_sourceFilePath);
|
||||
|
||||
var indent = " ";
|
||||
|
||||
|
@ -818,7 +796,7 @@ sealed class LottieFileProcessor
|
|||
|
||||
foreach (var (issue, uapVersionRange) in _translationIssues)
|
||||
{
|
||||
_reporter.WriteInfo(InfoType.Issue, IssueToString(_lottieFilePath, issue, uapVersionRange));
|
||||
_reporter.WriteInfo(InfoType.Issue, IssueToString(_jsonFilePath, issue, uapVersionRange));
|
||||
}
|
||||
|
||||
// NOTE: this is only reporting on the latest version in a multi-version translation.
|
||||
|
@ -853,5 +831,5 @@ sealed class LottieFileProcessor
|
|||
// Convert namespaces to a normalized form: replace "::" with ".".
|
||||
static string NormalizeNamespace(string @namespace) => @namespace.Replace("::", ".");
|
||||
|
||||
public override string ToString() => $"{nameof(LottieFileProcessor)} {_lottieFilePath}";
|
||||
public override string ToString() => $"{nameof(LottieJsonFileProcessor)} {_jsonFilePath}";
|
||||
}
|
|
@ -151,11 +151,11 @@ sealed class Program
|
|||
Parallel.ForEach(matchingInputFiles, ((string path, string relativePath) inputFile) =>
|
||||
#endif // DO_NOT_PROCESS_IN_PARALLEL
|
||||
{
|
||||
if (!LottieFileProcessor.ProcessLottieFile(
|
||||
if (!FileProcessor.ProcessFile(
|
||||
_options,
|
||||
_reporter,
|
||||
inputFile.path,
|
||||
System.IO.Path.Combine(outputFolder, inputFile.relativePath),
|
||||
Path.Combine(outputFolder, inputFile.relativePath),
|
||||
timestamp))
|
||||
{
|
||||
succeeded = false;
|
||||
|
@ -170,18 +170,18 @@ sealed class Program
|
|||
|
||||
if (_options.Languages.Contains(Lang.Stats))
|
||||
{
|
||||
// Write the stats. Stats are collected by the Reporter from each LottieFileProcessor
|
||||
// then written to files here after all of the LottieFileProcessors have finished.
|
||||
// Write the stats. Stats are collected by the Reporter from each FileProcessor
|
||||
// then written to files here after all of the FileProcessors have finished.
|
||||
foreach (var (dataTableName, columnNames, rows) in _reporter.GetDataTables())
|
||||
{
|
||||
var tsvFilePath = System.IO.Path.Combine(outputFolder, $"LottieGen_{dataTableName}.tsv");
|
||||
var tsvFilePath = Path.Combine(outputFolder, $"LottieGen_{dataTableName}.tsv");
|
||||
_reporter.WriteInfo("Writing stats to:");
|
||||
_reporter.WriteInfo(InfoType.FilePath, $" {tsvFilePath}");
|
||||
using var tsvFile = File.CreateText(tsvFilePath);
|
||||
tsvFile.WriteLine(string.Join("\t", columnNames));
|
||||
|
||||
// Sort the rows. This is necessary in order to ensure deterministic output
|
||||
// when multiple LottieFileProcessors are run in parallel.
|
||||
// when multiple FileProcessors are run in parallel.
|
||||
Array.Sort(rows, (a, b) =>
|
||||
{
|
||||
for (var i = 0; i < a.Length; i++)
|
||||
|
@ -206,10 +206,10 @@ sealed class Program
|
|||
return succeeded ? RunResult.Success : RunResult.Failure;
|
||||
}
|
||||
|
||||
static string MakeAbsolutePath(string path)
|
||||
{
|
||||
return System.IO.Path.IsPathRooted(path) ? path : System.IO.Path.Combine(Directory.GetCurrentDirectory(), path);
|
||||
}
|
||||
static string MakeAbsolutePath(string path) =>
|
||||
Path.IsPathRooted(path)
|
||||
? path
|
||||
: Path.Combine(Directory.GetCurrentDirectory(), path);
|
||||
|
||||
static void ShowHelp(Reporter.Writer writer)
|
||||
{
|
||||
|
@ -229,7 +229,7 @@ Usage: {0} -InputFile LOTTIEFILE -Language LANG [Other options]
|
|||
|
||||
OVERVIEW:
|
||||
Generates source code from Lottie files for playing in the AnimatedVisualPlayer.
|
||||
LOTTIEFILE is a Lottie .json file. LOTTIEFILE may contain wildcards.
|
||||
LOTTIEFILE is a Lottie .json file or .lottie file. LOTTIEFILE may contain wildcards.
|
||||
LANG is one of cs, cppcx, cppwinrt, lottieyaml, dgml, or stats.
|
||||
-Language LANG may be specified multiple times.
|
||||
|
||||
|
|
|
@ -91,6 +91,40 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.DotLottie
|
|||
return new MemoryStream(buffer, writable: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="DotLottieFile"/> that reads from the given <see cref="ZipArchive"/>.
|
||||
/// </summary>
|
||||
/// <param name="zipArchive">The <see cref="ZipArchive"/>.</param>
|
||||
/// <returns>A <see cref="DotLottieFile"/> or null on error.</returns>
|
||||
public static DotLottieFile? FromZipArchive(
|
||||
ZipArchive zipArchive)
|
||||
{
|
||||
// The manifest contains information about the animation.
|
||||
var manifestEntry = GetManifestEntry(zipArchive);
|
||||
|
||||
if (manifestEntry is null)
|
||||
{
|
||||
// Not a valid .lottie file.
|
||||
return null;
|
||||
}
|
||||
|
||||
var manifestBytes = new byte[manifestEntry.Length];
|
||||
using var manifestStream = manifestEntry.Open();
|
||||
var bytesRead = manifestStream.Read(
|
||||
manifestBytes,
|
||||
0,
|
||||
manifestBytes.Length);
|
||||
if (bytesRead != manifestBytes.Length)
|
||||
{
|
||||
// Failed to read the manifest
|
||||
return null;
|
||||
}
|
||||
|
||||
var manifest = Manifest.ParseManifest(manifestBytes);
|
||||
|
||||
return manifest is null ? null : new DotLottieFile(zipArchive, manifest);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="DotLottieFile"/> that reads from the given <see cref="ZipArchive"/>.
|
||||
/// </summary>
|
||||
|
@ -100,7 +134,7 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.DotLottie
|
|||
ZipArchive zipArchive)
|
||||
{
|
||||
// The manifest contains information about the animation.
|
||||
var manifestEntry = zipArchive.GetEntry("manifest.json");
|
||||
var manifestEntry = GetManifestEntry(zipArchive);
|
||||
|
||||
if (manifestEntry is null)
|
||||
{
|
||||
|
@ -132,6 +166,9 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.DotLottie
|
|||
|
||||
internal ZipArchive ZipArchive { get; }
|
||||
|
||||
static ZipArchiveEntry? GetManifestEntry(ZipArchive zipArchive)
|
||||
=> zipArchive.GetEntry("manifest.json");
|
||||
|
||||
ZipArchiveEntry GetEntryForPath(string path)
|
||||
{
|
||||
if (path.StartsWith("/"))
|
||||
|
|
|
@ -8,6 +8,10 @@ using System.IO;
|
|||
|
||||
namespace Microsoft.Toolkit.Uwp.UI.Lottie.DotLottie
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes an animation in a .lottie file. Animations
|
||||
/// are Lottie .json files.
|
||||
/// </summary>
|
||||
sealed class DotLottieFileAnimation
|
||||
{
|
||||
readonly DotLottieFile _owner;
|
||||
|
@ -26,8 +30,11 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.DotLottie
|
|||
|
||||
public bool Loop { get; }
|
||||
|
||||
public string Path => $"animations/{Id}.json";
|
||||
/// <summary>
|
||||
/// The path to the animation in the .lottie archive.
|
||||
/// </summary>
|
||||
public string Path => $"/animations/{Id}.json";
|
||||
|
||||
public Stream Open() => _owner.ZipArchive.GetEntry(Path).Open();
|
||||
public Stream Open() => _owner.ZipArchive.GetEntry(Path.Substring(1)).Open();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,25 +159,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.UIData.CodeGen
|
|||
/// </summary>
|
||||
protected IAnimatedVisualSourceInfo AnimatedVisualSourceInfo => this;
|
||||
|
||||
/// <summary>
|
||||
/// Takes a name and modifies it as necessary to be suited for use as a class name in languages such
|
||||
/// as C# and C++.
|
||||
/// Returns null on failure.
|
||||
/// </summary>
|
||||
/// <returns>A name, or null.</returns>
|
||||
public static string? TrySynthesizeClassName(string? name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove any leading punctuation.
|
||||
var prefixSize = name.TakeWhile(c => !char.IsLetterOrDigit(c)).Count();
|
||||
|
||||
return SanitizeTypeName(name.Substring(prefixSize));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the standard header text used to indicate that a file contains auto-generated content.
|
||||
/// </summary>
|
||||
|
@ -543,34 +524,6 @@ namespace Microsoft.Toolkit.Uwp.UI.Lottie.UIData.CodeGen
|
|||
return _currentAnimatedVisualGenerator!.CallFactoryFor(obj);
|
||||
}
|
||||
|
||||
// Makes the given name suitable for use as a class name in languages such as C# and C++.
|
||||
static string? SanitizeTypeName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the first character is not a letter, prepend an underscore.
|
||||
if (!char.IsLetter(name, 0))
|
||||
{
|
||||
name = "_" + name;
|
||||
}
|
||||
|
||||
// Replace any disallowed character with underscores.
|
||||
name =
|
||||
new string((from ch in name
|
||||
select char.IsLetterOrDigit(ch) ? ch : '_').ToArray());
|
||||
|
||||
// Remove any duplicated underscores.
|
||||
name = name.Replace("__", "_");
|
||||
|
||||
// Capitalize the first letter.
|
||||
name = name.ToUpperInvariant().Substring(0, 1) + name.Substring(1);
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
// Makes the given text suitable for use as a single line comment.
|
||||
static string? SanitizeCommentLine(string text)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче