write version from builder in xmp metadata #633

This commit is contained in:
Eliot Jones
2023-05-27 13:52:28 +01:00
parent 211b27062d
commit edf99acdf4
5 changed files with 138 additions and 114 deletions

View File

@@ -8,12 +8,15 @@ namespace UglyToad.PdfPig.Writer
{
internal static class PdfABaselineRuleBuilder
{
public static void Obey(Dictionary<NameToken, IToken> catalog, Func<IToken, IndirectReferenceToken> writerFunc,
public static void Obey(
Dictionary<NameToken, IToken> catalog,
Func<IToken, IndirectReferenceToken> writerFunc,
PdfDocumentBuilder.DocumentInformationBuilder documentInformationBuilder,
PdfAStandard archiveStandard)
PdfAStandard archiveStandard,
decimal version)
{
catalog[NameToken.OutputIntents] = OutputIntentsFactory.GetOutputIntentsArray(writerFunc);
var xmpStream = XmpWriter.GenerateXmpStream(documentInformationBuilder, 1.7m, archiveStandard);
var xmpStream = XmpWriter.GenerateXmpStream(documentInformationBuilder, version, archiveStandard);
var xmpObj = writerFunc(xmpStream);
catalog[NameToken.Metadata] = xmpObj;
}

View File

@@ -1,5 +1,6 @@
namespace UglyToad.PdfPig.Writer
{
using System;
using System.Collections.Generic;
using System.IO;
using Tokens;
@@ -8,7 +9,12 @@
{
private readonly Dictionary<byte[], IndirectReferenceToken> hashes = new Dictionary<byte[], IndirectReferenceToken>(new FNVByteComparison());
public PdfDedupStreamWriter(Stream stream, bool dispose, ITokenWriter tokenWriter = null) : base(stream, dispose, tokenWriter)
public PdfDedupStreamWriter(
Stream stream,
bool dispose,
ITokenWriter tokenWriter = null,
Action<decimal> recordVersion = null
) : base(stream, dispose, tokenWriter, recordVersion)
{
}

View File

@@ -9,12 +9,12 @@ namespace UglyToad.PdfPig.Writer
using Content;
using Core;
using Fonts;
using PdfPig.Actions;
using Actions;
using PdfPig.Fonts.TrueType;
using PdfPig.Fonts.Standard14Fonts;
using PdfPig.Fonts.TrueType.Parser;
using PdfPig.Outline;
using PdfPig.Outline.Destinations;
using Outline;
using Outline.Destinations;
using Tokenization.Scanner;
using Tokens;
@@ -30,6 +30,7 @@ namespace UglyToad.PdfPig.Writer
private readonly Dictionary<Guid, FontStored> fonts = new Dictionary<Guid, FontStored>();
private bool completed = false;
private int fontId = 0;
private decimal version = 1.7m;
private readonly static ArrayToken DefaultProcSet = new ArrayToken(new List<NameToken>
{
@@ -76,7 +77,7 @@ namespace UglyToad.PdfPig.Writer
/// </summary>
public PdfDocumentBuilder()
{
context = new PdfStreamWriter(new MemoryStream(), true);
context = new PdfStreamWriter(new MemoryStream(), true, recordVersion: x => version = x);
context.InitializePdf(1.7m);
}
@@ -86,7 +87,7 @@ namespace UglyToad.PdfPig.Writer
/// <param name="version">Pdf version to use in header.</param>
public PdfDocumentBuilder(decimal version)
{
context = new PdfStreamWriter(new MemoryStream(), true);
context = new PdfStreamWriter(new MemoryStream(), true, recordVersion: x => version = x);
context.InitializePdf(version);
}
@@ -103,12 +104,13 @@ namespace UglyToad.PdfPig.Writer
switch (type)
{
case PdfWriterType.ObjectInMemoryDedup:
context = new PdfDedupStreamWriter(stream, disposeStream, tokenWriter);
context = new PdfDedupStreamWriter(stream, disposeStream, tokenWriter, x => version = x);
break;
default:
context = new PdfStreamWriter(stream, disposeStream, tokenWriter);
context = new PdfStreamWriter(stream, disposeStream, tokenWriter, x => version = x);
break;
}
context.InitializePdf(version);
}
@@ -712,7 +714,7 @@ namespace UglyToad.PdfPig.Writer
{
Func<IToken, IndirectReferenceToken> writerFunc = x => context.WriteToken(x);
PdfABaselineRuleBuilder.Obey(catalogDictionary, writerFunc, DocumentInformation, ArchiveStandard);
PdfABaselineRuleBuilder.Obey(catalogDictionary, writerFunc, DocumentInformation, ArchiveStandard, version);
switch (ArchiveStandard)
{

View File

@@ -1,104 +1,115 @@
namespace UglyToad.PdfPig.Writer
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Core;
using Graphics.Operations;
using Tokens;
/// <summary>
/// This class would lazily flush all token. Allowing us to make changes to references without need to rewrite the whole stream
/// </summary>
internal class PdfStreamWriter : IPdfStreamWriter
{
protected const decimal DefaultVersion = 1.2m;
protected Dictionary<IndirectReference, long> offsets = new Dictionary<IndirectReference, long>();
protected bool DisposeStream { get; set; }
protected bool Initialized { get; set; }
protected int CurrentNumber { get; set; } = 1;
protected readonly ITokenWriter TokenWriter;
internal PdfStreamWriter(Stream baseStream, bool disposeStream = true, ITokenWriter tokenWriter = null)
{
Stream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
if (!baseStream.CanWrite)
{
throw new ArgumentException("Output stream must be writable");
}
DisposeStream = disposeStream;
TokenWriter = tokenWriter ?? new TokenWriter();
}
namespace UglyToad.PdfPig.Writer
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using Core;
using Graphics.Operations;
using Tokens;
/// <summary>
/// This class would lazily flush all token. Allowing us to make changes to references without need to rewrite the whole stream
/// </summary>
internal class PdfStreamWriter : IPdfStreamWriter
{
private readonly Action<decimal> recordVersion;
protected const decimal DefaultVersion = 1.2m;
protected Dictionary<IndirectReference, long> offsets = new Dictionary<IndirectReference, long>();
protected bool DisposeStream { get; set; }
protected bool Initialized { get; set; }
protected int CurrentNumber { get; set; } = 1;
protected readonly ITokenWriter TokenWriter;
public Stream Stream { get; protected set; }
public bool AttemptDeduplication { get; set; } = true;
public virtual IndirectReferenceToken WriteToken(IToken token)
{
if (!Initialized)
{
InitializePdf(DefaultVersion);
}
var ir = ReserveObjectNumber();
offsets.Add(ir.Data, Stream.Position);
var obj = new ObjectToken(Stream.Position, ir.Data, token);
TokenWriter.WriteToken(obj, Stream);
return ir;
}
public virtual IndirectReferenceToken WriteToken(IToken token, IndirectReferenceToken indirectReference)
{
if (!Initialized)
{
InitializePdf(DefaultVersion);
}
offsets.Add(indirectReference.Data, Stream.Position);
var obj = new ObjectToken(Stream.Position, indirectReference.Data, token);
TokenWriter.WriteToken(obj, Stream);
return indirectReference;
}
public IndirectReferenceToken ReserveObjectNumber()
{
return new IndirectReferenceToken(new IndirectReference(CurrentNumber++, 0));
}
public void InitializePdf(decimal version)
{
WriteString($"%PDF-{version.ToString("0.0", CultureInfo.InvariantCulture)}", Stream);
Stream.WriteText("%");
Stream.WriteByte(169);
Stream.WriteByte(205);
Stream.WriteByte(196);
Stream.WriteByte(210);
Stream.WriteNewLine();
Initialized = true;
}
public void CompletePdf(IndirectReferenceToken catalogReference, IndirectReferenceToken documentInformationReference = null)
{
TokenWriter.WriteCrossReferenceTable(offsets, catalogReference.Data, Stream, documentInformationReference?.Data);
}
private static void WriteString(string text, Stream stream)
{
var bytes = OtherEncodings.StringAsLatin1Bytes(text);
stream.Write(bytes, 0, bytes.Length);
stream.WriteNewLine();
}
public void Dispose()
{
if (DisposeStream)
{
Stream?.Dispose();
}
Stream = null;
}
}
}
internal PdfStreamWriter(
Stream baseStream,
bool disposeStream = true,
ITokenWriter tokenWriter = null,
Action<decimal> recordVersion = null)
{
Stream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
if (!baseStream.CanWrite)
{
throw new ArgumentException("Output stream must be writable");
}
this.recordVersion = recordVersion;
DisposeStream = disposeStream;
TokenWriter = tokenWriter ?? new TokenWriter();
}
public virtual IndirectReferenceToken WriteToken(IToken token)
{
if (!Initialized)
{
InitializePdf(DefaultVersion);
}
var ir = ReserveObjectNumber();
offsets.Add(ir.Data, Stream.Position);
var obj = new ObjectToken(Stream.Position, ir.Data, token);
TokenWriter.WriteToken(obj, Stream);
return ir;
}
public virtual IndirectReferenceToken WriteToken(IToken token, IndirectReferenceToken indirectReference)
{
if (!Initialized)
{
InitializePdf(DefaultVersion);
}
offsets.Add(indirectReference.Data, Stream.Position);
var obj = new ObjectToken(Stream.Position, indirectReference.Data, token);
TokenWriter.WriteToken(obj, Stream);
return indirectReference;
}
public IndirectReferenceToken ReserveObjectNumber()
{
return new IndirectReferenceToken(new IndirectReference(CurrentNumber++, 0));
}
public void InitializePdf(decimal version)
{
recordVersion?.Invoke(version);
WriteString($"%PDF-{version.ToString("0.0", CultureInfo.InvariantCulture)}", Stream);
Stream.WriteText("%");
Stream.WriteByte(169);
Stream.WriteByte(205);
Stream.WriteByte(196);
Stream.WriteByte(210);
Stream.WriteNewLine();
Initialized = true;
}
public void CompletePdf(IndirectReferenceToken catalogReference, IndirectReferenceToken documentInformationReference = null)
{
TokenWriter.WriteCrossReferenceTable(offsets, catalogReference.Data, Stream, documentInformationReference?.Data);
}
private static void WriteString(string text, Stream stream)
{
var bytes = OtherEncodings.StringAsLatin1Bytes(text);
stream.Write(bytes, 0, bytes.Length);
stream.WriteNewLine();
}
public void Dispose()
{
if (DisposeStream)
{
Stream?.Dispose();
}
Stream = null;
}
}
}

View File

@@ -7,6 +7,8 @@ using UglyToad.PdfPig.Tokens;
namespace UglyToad.PdfPig.Writer.Xmp
{
using System.Globalization;
internal static class XmpWriter
{
private const string Xmptk = "Adobe XMP Core 5.6-c014 79.156797, 2014/08/20-09:53:02 ";
@@ -67,7 +69,7 @@ namespace UglyToad.PdfPig.Writer.Xmp
AddElementsForSchema(rdfDescriptionElement, AdobePdfPrefix, AdobePdfNamespace, builder,
new List<SchemaMapper>
{
new SchemaMapper("PDFVersion", b => "1.7"),
new SchemaMapper("PDFVersion", b => version.ToString("F1", CultureInfo.InvariantCulture)),
new SchemaMapper("Producer", b => b.Producer)
});