mirror of
				https://github.com/Frooodle/Stirling-PDF.git
				synced 2025-11-01 01:21:18 +01:00 
			
		
		
		
	Update DeveloperGuide.md
This commit is contained in:
		
							parent
							
								
									22b727df17
								
							
						
					
					
						commit
						bd36841094
					
				@ -298,3 +298,260 @@ When contributing translations:
 | 
				
			|||||||
3. The PR checks will verify consistency in language file updates.
 | 
					3. The PR checks will verify consistency in language file updates.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Remember to test your changes thoroughly to ensure they don't break any existing functionality.
 | 
					Remember to test your changes thoroughly to ensure they don't break any existing functionality.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Code examples
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Overview of Thymeleaf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thymeleaf is a  server-side Java HTML template engine. It is used in Stirling-PDF to render dynamic web pages. Thymeleaf integrates heavily with Spring Boot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Thymeleaf overview
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In Stirling-PDF, Thymeleaf is used to create HTML templates that are rendered on the server side. These templates are located in the `src/main/resources/templates` directory. Thymeleaf templates use a combination of HTML and special Thymeleaf attributes to dynamically generate content.
 | 
				
			||||||
 | 
					Some examples of this are 
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
 | 
				
			||||||
 | 
					or
 | 
				
			||||||
 | 
					<th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Where it uses the th:block, th: indicating its a special thymeleaf element to be used serverside in generating the html, and block being the actual element type.
 | 
				
			||||||
 | 
					In this case we are inserting the ``navbar`` entry within the ``fragments/navbar.html`` fragment into the ``th:block`` element.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					They can be more complex such as
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<th:block th:insert="~{fragments/common :: head(title=#{pageExtracter.title}, header=#{pageExtracter.header})}"></th:block>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					Which is the same as above but passes the parameters title and header into the fragment common.html to be used in its HTML generation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thymeleaf can also be used to loop through objects or pass things from java side into html side.
 | 
				
			||||||
 | 
					```java
 | 
				
			||||||
 | 
					 @GetMapping
 | 
				
			||||||
 | 
					       public String newFeaturePage(Model model) {
 | 
				
			||||||
 | 
					           model.addAttribute("exampleData", exampleData);
 | 
				
			||||||
 | 
					           return "new-feature";
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					in above example if exampleData is a list of  plain java objects of class Person and within it you had id, name, age etc. You can reference it like so
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<tbody>
 | 
				
			||||||
 | 
					   <!-- Use th:each to iterate over the list -->
 | 
				
			||||||
 | 
					   <tr th:each="person : ${exampleData}">
 | 
				
			||||||
 | 
					       <td th:text="${person.id}"></td>
 | 
				
			||||||
 | 
					       <td th:text="${person.name}"></td>
 | 
				
			||||||
 | 
					       <td th:text="${person.age}"></td>
 | 
				
			||||||
 | 
					       <td th:text="${person.email}"></td>
 | 
				
			||||||
 | 
					   </tr>
 | 
				
			||||||
 | 
					</tbody>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					This would generate n entries of tr for each person in exampleData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Adding a New Feature to the Backend (API)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **Create a New Controller:**
 | 
				
			||||||
 | 
					   - Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/api` directory.
 | 
				
			||||||
 | 
					   - Annotate the class with `@RestController` and `@RequestMapping` to define the API endpoint.
 | 
				
			||||||
 | 
					   - Ensure to add API documentation annotations like `@Tag(name = "General", description = "General APIs")` and `@Operation(summary = "Crops a PDF document", description = "This operation takes an input PDF file and crops it according to the given coordinates. Input:PDF Output:PDF Type:SISO")`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   ```java
 | 
				
			||||||
 | 
					   package stirling.software.SPDF.controller.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.GetMapping;
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.RequestMapping;
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.RestController;
 | 
				
			||||||
 | 
					   import io.swagger.v3.oas.annotations.Operation;
 | 
				
			||||||
 | 
					   import io.swagger.v3.oas.annotations.tags.Tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   @RestController
 | 
				
			||||||
 | 
					   @RequestMapping("/api/v1/new-feature")
 | 
				
			||||||
 | 
					   @Tag(name = "General", description = "General APIs")
 | 
				
			||||||
 | 
					   public class NewFeatureController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       @GetMapping
 | 
				
			||||||
 | 
					       @Operation(summary = "New Feature", description = "This is a new feature endpoint.")
 | 
				
			||||||
 | 
					       public String newFeature() {
 | 
				
			||||||
 | 
					           return "NewFeatureResponse"; // This refers to the NewFeatureResponse.html template presenting the user with the generated html from that file when they navigate to /api/v1/new-feature
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. **Define the Service Layer:** (Not required but often useful)
 | 
				
			||||||
 | 
					   - Create a new service class in the `src/main/java/stirling/software/SPDF/service` directory.
 | 
				
			||||||
 | 
					   - Implement the business logic for the new feature.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   ```java
 | 
				
			||||||
 | 
					   package stirling.software.SPDF.service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   import org.springframework.stereotype.Service;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   @Service
 | 
				
			||||||
 | 
					   public class NewFeatureService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       public String getNewFeatureData() {
 | 
				
			||||||
 | 
					           // Implement business logic here
 | 
				
			||||||
 | 
					           return "New Feature Data";
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2b. **Integrate the Service with the Controller:**
 | 
				
			||||||
 | 
					   - Autowire the service class in the controller and use it to handle the API request.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   ```java
 | 
				
			||||||
 | 
					   package stirling.software.SPDF.controller.api;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.GetMapping;
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.RequestMapping;
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.RestController;
 | 
				
			||||||
 | 
					   import stirling.software.SPDF.service.NewFeatureService;
 | 
				
			||||||
 | 
					   import io.swagger.v3.oas.annotations.Operation;
 | 
				
			||||||
 | 
					   import io.swagger.v3.oas.annotations.tags.Tag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   @RestController
 | 
				
			||||||
 | 
					   @RequestMapping("/api/v1/new-feature")
 | 
				
			||||||
 | 
					   @Tag(name = "General", description = "General APIs")
 | 
				
			||||||
 | 
					   public class NewFeatureController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       @Autowired
 | 
				
			||||||
 | 
					       private NewFeatureService newFeatureService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       @GetMapping
 | 
				
			||||||
 | 
					       @Operation(summary = "New Feature", description = "This is a new feature endpoint.")
 | 
				
			||||||
 | 
					       public String newFeature() {
 | 
				
			||||||
 | 
					           return newFeatureService.getNewFeatureData();
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Adding a New Feature to the Frontend (UI)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **Create a New Thymeleaf Template:**
 | 
				
			||||||
 | 
					   - Create a new HTML file in the `src/main/resources/templates` directory.
 | 
				
			||||||
 | 
					   - Use Thymeleaf attributes to dynamically generate content.
 | 
				
			||||||
 | 
					   - Use `extract-page.html` as a base example for the HTML template, useful to ensure importing of the general layout, navbar and footer.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   ```html
 | 
				
			||||||
 | 
					   <!DOCTYPE html>
 | 
				
			||||||
 | 
					   <html th:lang="${#locale.language}" th:dir="#{language.direction}" th:data-language="${#locale.toString()}" xmlns:th="https://www.thymeleaf.org">
 | 
				
			||||||
 | 
					     <head>
 | 
				
			||||||
 | 
					     <th:block th:insert="~{fragments/common :: head(title=#{newFeature.title}, header=#{newFeature.header})}"></th:block>
 | 
				
			||||||
 | 
					     </head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     <body>
 | 
				
			||||||
 | 
					       <div id="page-container">
 | 
				
			||||||
 | 
					         <div id="content-wrap">
 | 
				
			||||||
 | 
					           <th:block th:insert="~{fragments/navbar.html :: navbar}"></th:block>
 | 
				
			||||||
 | 
					           <br><br>
 | 
				
			||||||
 | 
					           <div class="container">
 | 
				
			||||||
 | 
					             <div class="row justify-content-center">
 | 
				
			||||||
 | 
					               <div class="col-md-6 bg-card">
 | 
				
			||||||
 | 
					                 <div class="tool-header">
 | 
				
			||||||
 | 
					                   <span class="material-symbols-rounded tool-header-icon organize">upload</span>
 | 
				
			||||||
 | 
					                   <span class="tool-header-text" th:text="#{newFeature.header}"></span>
 | 
				
			||||||
 | 
					                 </div>
 | 
				
			||||||
 | 
					                 <form th:action="@{'/api/v1/new-feature'}" method="post" enctype="multipart/form-data">
 | 
				
			||||||
 | 
					                   <div th:replace="~{fragments/common :: fileSelector(name='fileInput', multipleInputsForSingleRequest=false, accept='application/pdf')}"></div>
 | 
				
			||||||
 | 
					                   <input type="hidden" id="customMode" name="customMode" value="">
 | 
				
			||||||
 | 
					                   <div class="mb-3">
 | 
				
			||||||
 | 
					                     <label for="featureInput" th:text="#{newFeature.prompt}"></label>
 | 
				
			||||||
 | 
					                     <input type="text" class="form-control" id="featureInput" name="featureInput" th:placeholder="#{newFeature.placeholder}" required>
 | 
				
			||||||
 | 
					                   </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                   <button type="submit" id="submitBtn" class="btn btn-primary" th:text="#{newFeature.submit}"></button>
 | 
				
			||||||
 | 
					                 </form>
 | 
				
			||||||
 | 
					               </div>
 | 
				
			||||||
 | 
					             </div>
 | 
				
			||||||
 | 
					           </div>
 | 
				
			||||||
 | 
					         </div>
 | 
				
			||||||
 | 
					         <th:block th:insert="~{fragments/footer.html :: footer}"></th:block>
 | 
				
			||||||
 | 
					       </div>
 | 
				
			||||||
 | 
					     </body>
 | 
				
			||||||
 | 
					   </html>
 | 
				
			||||||
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					2. **Create a New Controller for the UI:**
 | 
				
			||||||
 | 
					   - Create a new Java class in the `src/main/java/stirling/software/SPDF/controller/ui` directory.
 | 
				
			||||||
 | 
					   - Annotate the class with `@Controller` and `@RequestMapping` to define the UI endpoint.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   ```java
 | 
				
			||||||
 | 
					   package stirling.software.SPDF.controller.ui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   import org.springframework.beans.factory.annotation.Autowired;
 | 
				
			||||||
 | 
					   import org.springframework.stereotype.Controller;
 | 
				
			||||||
 | 
					   import org.springframework.ui.Model;
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.GetMapping;
 | 
				
			||||||
 | 
					   import org.springframework.web.bind.annotation.RequestMapping;
 | 
				
			||||||
 | 
					   import stirling.software.SPDF.service.NewFeatureService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   @Controller
 | 
				
			||||||
 | 
					   @RequestMapping("/new-feature")
 | 
				
			||||||
 | 
					   public class NewFeatureUIController {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       @Autowired
 | 
				
			||||||
 | 
					       private NewFeatureService newFeatureService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       @GetMapping
 | 
				
			||||||
 | 
					       public String newFeaturePage(Model model) {
 | 
				
			||||||
 | 
					           model.addAttribute("newFeatureData", newFeatureService.getNewFeatureData());
 | 
				
			||||||
 | 
					           return "new-feature";
 | 
				
			||||||
 | 
					       }
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					3. **Update the Navigation Bar:**
 | 
				
			||||||
 | 
					   - Add a link to the new feature page in the navigation bar.
 | 
				
			||||||
 | 
					   - Update the `src/main/resources/templates/fragments/navbar.html` file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   ```html
 | 
				
			||||||
 | 
					   <li class="nav-item">
 | 
				
			||||||
 | 
					       <a class="nav-link" th:href="@{/new-feature}">New Feature</a>
 | 
				
			||||||
 | 
					   </li>
 | 
				
			||||||
 | 
					   ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Adding New Translations to Existing Language Files in Stirling-PDF
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					When adding a new feature or modifying existing ones in Stirling-PDF, you'll need to add new translation entries to the existing language files. Here's a step-by-step guide:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 1. Locate Existing Language Files
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Find the existing `messages.properties` files in the `src/main/resources` directory. You'll see files like:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- `messages.properties` (default, usually English)
 | 
				
			||||||
 | 
					- `messages_en_GB.properties`
 | 
				
			||||||
 | 
					- `messages_fr.properties`
 | 
				
			||||||
 | 
					- `messages_de.properties`
 | 
				
			||||||
 | 
					- etc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 2. Add New Translation Entries
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Open each of these files and add your new translation entries. For example, if you're adding a new feature called "PDF Splitter", 
 | 
				
			||||||
 | 
					Use descriptive, hierarchical keys (e.g., `feature.element.description`)
 | 
				
			||||||
 | 
					you might add:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```properties
 | 
				
			||||||
 | 
					pdfSplitter.title=PDF Splitter
 | 
				
			||||||
 | 
					pdfSplitter.description=Split your PDF into multiple documents
 | 
				
			||||||
 | 
					pdfSplitter.button.split=Split PDF
 | 
				
			||||||
 | 
					pdfSplitter.input.pages=Enter page numbers to split
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add these entries to the default GB language file and any others you wish, translating the values as appropriate for each language.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 3. Use Translations in Thymeleaf Templates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In your Thymeleaf templates, use the `#{key}` syntax to reference the new translations:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```html
 | 
				
			||||||
 | 
					<h1 th:text="#{pdfSplitter.title}">PDF Splitter</h1>
 | 
				
			||||||
 | 
					<p th:text="#{pdfSplitter.description}">Split your PDF into multiple documents</p>
 | 
				
			||||||
 | 
					<input type="text" th:placeholder="#{pdfSplitter.input.pages}">
 | 
				
			||||||
 | 
					<button th:text="#{pdfSplitter.button.split}">Split PDF</button>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Remember, never hard-code text in your templates or Java code. Always use translation keys to ensure proper localization.
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user