Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 34 additions & 15 deletions src/ShapeClipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ interface PasteContext {
binaryModifications: Map<string, Uint8Array>;
contentTypesDocument: XMLDocument;
clonedParts: Map<string, string>;
parsedDocuments: Map<string, XMLDocument>;
}

export function createSlideObjectClipboard(
Expand Down Expand Up @@ -122,7 +123,8 @@ export async function pasteSlideObjects(
textModifications: new Map(),
binaryModifications: new Map(),
contentTypesDocument,
clonedParts: new Map()
clonedParts: new Map(),
parsedDocuments: new Map()
};
const destinationShapeTree = getShapeTree(destinationSlideDocument);
const shapeIndexes: number[] = [];
Expand All @@ -134,11 +136,15 @@ export async function pasteSlideObjects(

offsetShape(clonedShape, offsetEmu, offsetEmu);
assignUniqueNonVisualIds(destinationSlideDocument, clonedShape);
await copyShapeRelationships(sourceSlidePath, destinationSlidePath, clonedShape, context);
copyShapeRelationships(sourceSlidePath, destinationSlidePath, clonedShape, context);
destinationShapeTree.appendChild(clonedShape);
shapeIndexes.push(getShapeChildren(destinationShapeTree).length - 1);
}

for (const [partPath, doc] of context.parsedDocuments) {
context.textModifications.set(partPath, serializeXml(doc));
}

context.textModifications.set(destinationSlidePath, serializeXml(destinationSlideDocument));
context.textModifications.set('[Content_Types].xml', serializeXml(contentTypesDocument));
const buffer = await buildZip(
Expand Down Expand Up @@ -224,22 +230,37 @@ function fallbackUuid(): string {
});
}

async function copyShapeRelationships(
function getOrParseRelationshipsDocument(
destinationRelationshipsPath: string,
context: PasteContext
): XMLDocument {
const existing = context.parsedDocuments.get(destinationRelationshipsPath);
if (existing) return existing;

const xmlText =
context.textModifications.get(destinationRelationshipsPath) ??
context.destination.textFiles.get(destinationRelationshipsPath);
const doc = xmlText
? parseXml(xmlText, destinationRelationshipsPath)
: createRelationshipsDocument();
context.parsedDocuments.set(destinationRelationshipsPath, doc);
return doc;
}

function copyShapeRelationships(
sourceSlidePath: string,
destinationSlidePath: string,
clonedShape: Element,
context: PasteContext
): Promise<void> {
): void {
const sourceRelationshipsPath = getRelationshipsPath(sourceSlidePath);
const sourceRelationshipsXml = context.source.textFiles.get(sourceRelationshipsPath);
const relationshipAttributes = getRelationshipAttributes(clonedShape);
if (!sourceRelationshipsXml || relationshipAttributes.length === 0) return;

const sourceRelationships = parseXml(sourceRelationshipsXml, sourceRelationshipsPath);
const destinationRelationshipsPath = getRelationshipsPath(destinationSlidePath);
const destinationRelationships = context.destination.textFiles.has(destinationRelationshipsPath)
? parseXml(getRequiredTextFile(context.destination, destinationRelationshipsPath), destinationRelationshipsPath)
: createRelationshipsDocument();
const destinationRelationships = getOrParseRelationshipsDocument(destinationRelationshipsPath, context);

for (const attribute of relationshipAttributes) {
const sourceRelationship = findRelationship(sourceRelationships, attribute.value);
Expand All @@ -253,7 +274,7 @@ async function copyShapeRelationships(
const target = clonedRelationship.getAttribute('Target');
if (target && clonedRelationship.getAttribute('TargetMode') !== 'External') {
const sourceTargetPath = resolvePartPath(sourceSlidePath, target);
const destinationTargetPath = await ensureRelatedPart(
const destinationTargetPath = ensureRelatedPart(
sourceTargetPath,
context,
isChartRelationship(clonedRelationship)
Expand All @@ -263,8 +284,6 @@ async function copyShapeRelationships(

destinationRelationships.documentElement.appendChild(clonedRelationship);
}

context.textModifications.set(destinationRelationshipsPath, serializeXml(destinationRelationships));
}

function getRelationshipAttributes(element: Element): Attr[] {
Expand Down Expand Up @@ -300,11 +319,11 @@ function isChartRelationship(relationship: Element): boolean {
return relationship.getAttribute('Type')?.endsWith('/chart') ?? false;
}

async function ensureRelatedPart(
function ensureRelatedPart(
sourcePartPath: string,
context: PasteContext,
forceClone: boolean
): Promise<string> {
): string {
const cached = context.clonedParts.get(sourcePartPath);
if (cached) return cached;

Expand Down Expand Up @@ -336,16 +355,16 @@ async function ensureRelatedPart(
if (!target || relationship.getAttribute('TargetMode') === 'External') continue;

const sourceTargetPath = resolvePartPath(sourcePartPath, target);
const destinationTargetPath = await ensureRelatedPart(
const destinationTargetPath = ensureRelatedPart(
sourceTargetPath,
context,
shouldCloneDependency(sourcePartPath, sourceTargetPath)
);
relationship.setAttribute('Target', getRelativePartPath(destinationPartPath, destinationTargetPath));
}
context.textModifications.set(
context.parsedDocuments.set(
getRelationshipsPath(destinationPartPath),
serializeXml(relationships)
relationships
);
}

Expand Down