This report details my contributions to enabling master page copying in LibreOffice Impress, addressing a 13-year-old bug (Bug 45617) that prevented users from duplicating master page templates. My work, delivered through two Gerrit patches (Change 179832 and Change 182206), was integrated into LibreOffice 25.8.0, released in 2025. The changes involved refactoring a complex codebase and implementing robust master page copying functionality, significantly improving Impress’s usability for creating presentation templates.
2025-04-29.07-00-23.mp4
The bug, reported in 2012, caused master page copying in the slide sorter view (“View->Master->Slide Master”) to fail silently, with no duplicate created. The root cause was the SdDrawDocument::InsertBookmarkAsPage function in sd/source/core/drawdoc3.cxx, a 555-line monolithic method handling multiple page insertion scenarios. Its complexity hindered modifications, necessitating extensive refactoring to enable master page copying.
My work consisted of two phases: refactoring the codebase for maintainability and implementing the master page copying feature. Below are the technical details of each phase, based on the patches and commit messages.
Phase 1: Refactoring SdDrawDocument::InsertBookmarkAsPage (Gerrit Change 179832, Commit 4db90da)
The SdDrawDocument::InsertBookmarkAsPage function was a 555-line method responsible for page insertions, style management, and dependency handling across scenarios like pasting, file insertion, and drag-drop. Its complexity made it difficult to extend, particularly for master page operations. I refactored it to create a modular, maintainable codebase.
- Function Decomposition:
- Split the monolithic function into smaller methods, each with a single responsibility:
insertAllPages: Inserts all pages from a source document.insertSelectedPages: Inserts only selected pages.initBookmarkDoc: Initializes the bookmark document for insertion.getPageProperties: Retrieves properties of pages to be inserted.determineScaleObjects: Determines scaling for inserted objects.collectLayoutsToTransfer: Collects layouts for style transfer.transferLayoutStyles: Transfers layout styles to inserted pages.copyStyles: Copies styles between documents.removeDuplicateMasterPages: Removes redundant master pages.updateInsertedPages: Updates properties of inserted pages.renameObjectStylesIfNeeded: Renames styles to avoid conflicts.cleanupStyles: Cleans up unused styles.beginUndoActionandendUndoAction: Manages undo/redo operations.
- Ensured each method adhered to the single-responsibility principle, improving readability and testability.
- Split the monolithic function into smaller methods, each with a single responsibility:
- Parameter Structures:
- Introduced structures to organize function arguments:
StyleTransferContext: Manages style transfer parameters.InsertBookmarkOptions: Encapsulates options like merge behavior.PageProperties: Stores page properties.DocumentPageCounts: Tracks page counts, including master pages.PageInsertionParams: Configures insertion parameters.
- Replaced long parameter lists with these structures, simplifying function signatures.
- Introduced structures to organize function arguments:
- Specialized Variants:
- Created function variants for different insertion scenarios:
ForPaste: Handles clipboard-based pasting.ForFileInsert: Manages file-based page insertion.ForDragDrop: Supports drag-and-drop operations.ForPageLinks: Processes page links.ForInternalOps: Handles internal document operations.ForDocumentImport: Imports pages from another document.
- Isolated master page logic to enable targeted modifications.
- Created function variants for different insertion scenarios:
- Modified Functions:
- Updated related functions to use the new modular structure:
PasteBookmarkAsPageResolvePageLinksImportDocumentPagesInsertFileAsPageDropBookmarkAsPageCopyOrMovePagesWithinDocument
- Updated related functions to use the new modular structure:
- Files Changed:
sd/inc/drawdoc.hxx: Updated header definitions for new methods and structures.sd/source/core/drawdoc3.cxx: Implemented refactored logic.sd/qa/unit/misc-tests.cxx: Adjusted unit tests for compatibility.sd/qa/unit/uiimpress.cxx: Updated UI-related tests.sd/source/core/pglink.cxx: Modified page link handling.sd/source/ui/app/sdxfer.cxx: Updated transfer logic.sd/source/ui/func/fuinsfil.cxx: Adjusted file insertion functions.sd/source/ui/slidesorter/controller/SlsClipboard.cxx: Updated clipboard operations.sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx: Modified clipboard header.sd/source/ui/view/ViewClipboard.cxx: Updated view clipboard logic.
This refactoring reduced complexity, making the codebase easier to extend and maintain. It provided a clean foundation for implementing master page copying, minimizing the risk of regressions.
Phase 2: Implementing Master Page Copying (Gerrit Change 182206, Commit 0d8f5429)
Using the refactored codebase, I implemented the ability to copy master pages, enabling users to duplicate templates in the slide sorter view. This involved adding new options, updating insertion logic, enhancing naming conventions, and ensuring reliability through unit tests.
- New Option:
bMergeMasterPagesOnly:- Added a boolean flag to
InsertBookmarkOptionsto specify that only master pages should be merged during copy operations. - Enabled precise control, ensuring regular slides remained unaffected.
- Added a boolean flag to
- Function Updates:
- Modified core insertion functions to incorporate
bMergeMasterPagesOnly:ForPaste: Enabled clipboard-based master page copying.ForFileInsert: Supported importing master pages from external documents.ForDragDrop: Facilitated drag-and-drop copying in the slide sorter.ForPageLinks: Ensured linked pages reflected master page changes.ForInternalOps: Streamlined internal master page operations.ForDocumentImport: Handled master page imports during document merging.
- Added conditional checks:
if (options.bMergeMasterPagesOnly) { handleMasterPageInsertion(options); } else { handleRegularSlideInsertion(options); }
- Modified core insertion functions to incorporate
- Logic Adjustments:
- Updated insertion functions to support master page copying:
insertAllPages: Processed master pages whenbMergeMasterPagesOnlyis true.PasteBookmarkAsPage: Handled master page pasting.ResolvePageLinks: Resolved master page links.ImportDocumentPages: Supported master page imports.InsertFileAsPage: Handled file-based master page insertion.DropBookmarkAsPage: Enabled drag-drop master page copying.CopyOrMovePagesWithinDocument: Supported intra-document master page copying.
- Updated insertion functions to support master page copying:
- Naming Logic:
- Implemented a system to generate unique names for copied master pages:
- Appended incremental numbers (e.g., “1_MasterName”, “2_MasterName”).
- Added methods:
GetBaseLayoutName: Extracts the base name of a layout.GenerateNewLayoutName: Generates a unique name for the copied master page.
- Implemented a system to generate unique names for copied master pages:
- New Method:
AddNewMasterPageFromExisting:- Created to copy an existing master page:
void SdDrawDocument::AddNewMasterPageFromExisting(SdPage* pSourcePage, const OUString& sBaseName) { SdPage* pNewPage = createNewMasterPage(); copyAttributes(pSourcePage, pNewPage); OUString sNewName = GenerateNewLayoutName(sBaseName); pNewPage->setName(sNewName); insertMasterPage(pNewPage); }
- Copied attributes (e.g., backgrounds, placeholders, styles) and dependencies.
- Created to copy an existing master page:
- Unit Tests:
- Added tests in
sd/qa/unit/uiimpress.cxx:- Single master page duplication.
- Multiple master page copies with dependencies.
- Edge cases: empty presentations, corrupted data, maximum page limits.
- Example test case:
void testMasterPageCopy() { SdDrawDocument doc; SdPage* pMaster = doc.createMasterPage("Default"); InsertBookmarkOptions options; options.bMergeMasterPagesOnly = true; doc.AddNewMasterPageFromExisting(pMaster, "Default"); CPPUNIT_ASSERT_EQUAL(2, doc.getMasterPageCount()); SdPage* pCopied = doc.getMasterPage(1); CPPUNIT_ASSERT_EQUAL(OUString("1_Default"), pCopied->getName()); }
- Added tests in
- UI Integration:
- Updated slide sorter components in
sd/source/ui/slidesorter/controller/SlsClipboard.cxxto passbMergeMasterPagesOnly. - Added visual feedback (e.g., highlighting copied master pages).
- Updated slide sorter components in
- Structure Modification:
- Modified
DocumentPageCountsto includenNewMPageCountfor tracking new master pages:struct DocumentPageCounts { sal_uInt16 nPageCount; sal_uInt16 nMasterPageCount; sal_uInt16 nNewMPageCount; };
- Modified
- Files Changed:
sd/source/core/drawdoc3.cxx: Implemented master page copying logic.sd/qa/unit/uiimpress.cxx: Added unit tests.sd/source/ui/slidesorter/controller/SlsClipboard.cxx: Updated UI clipboard handling.
This implementation enabled seamless master page copying, with unique naming to prevent conflicts and unit tests ensuring reliability. The feature integrated smoothly with the UI, enhancing user experience.
- Execution Time: Copying operations completed in milliseconds, even for presentations with dozens of master pages.
- Memory Usage: The refactored code maintained a small memory footprint, with
nNewMPageCountpreventing leaks. - Overhead: Modular functions reduced computational overhead, ensuring no significant performance impact.
- Code Complexity: Decomposing the 555-line
InsertBookmarkAsPagefunction required careful analysis to preserve existing functionality. - Dependency Handling: Copying master pages involved managing dependencies (e.g., styles, layouts), necessitating precise logic.
- Edge Cases: Comprehensive unit tests addressed scenarios like corrupted data or large documents.
| Phase | Commit | Key Changes | Files Modified |
|---|---|---|---|
| Refactoring | 4db90da | Decomposed InsertBookmarkAsPage into modular functions; added parameter structures; created insertion variants |
sd/inc/drawdoc.hxx, sd/source/core/drawdoc3.cxx, sd/qa/unit/misc-tests.cxx, sd/qa/unit/uiimpress.cxx, sd/source/core/pglink.cxx, sd/source/ui/app/sdxfer.cxx, sd/source/ui/func/fuinsfil.cxx, sd/source/ui/slidesorter/controller/SlsClipboard.cxx, sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx, sd/source/ui/view/ViewClipboard.cxx |
| Feature Implementation | 0d8f5429 | Added bMergeMasterPagesOnly; updated insertion functions; implemented naming logic; added AddNewMasterPageFromExisting; added unit tests |
sd/source/core/drawdoc3.cxx, sd/qa/unit/uiimpress.cxx, sd/source/ui/slidesorter/controller/SlsClipboard.cxx |
My contributions through Gerrit change 179832 and Gerrit change 182206 enabled master page copying in LibreOffice Impress, resolving a critical limitation. The refactoring phase transformed a complex codebase into a modular structure, while the implementation phase delivered a robust, user-friendly feature with comprehensive testing. These changes, integrated into LibreOffice 25.8.0, enhance Impress’s functionality and maintainability, benefiting users and future developers.