Skip to content

Commit c57a2a4

Browse files
authored
Add v2 client-side PDF text editor (#6500)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have run `task check` to verify linters, typechecks, and tests pass - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/DeveloperGuide.md#7-testing) for more details.
1 parent d30faf2 commit c57a2a4

301 files changed

Lines changed: 64524 additions & 8126 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/core/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,9 @@ tasks.register('copyFrontendAssets', Copy) {
306306
// Exclude files that conflict with backend static resources
307307
exclude 'robots.txt' // Backend already has this
308308
exclude 'favicon.ico' // Backend already has this
309+
// Backend ships its own NotoSans-Regular.ttf here and it is git-tracked;
310+
// letting the editor's copy win would dirty the source tree on every build.
311+
exclude 'fonts/NotoSans-Regular.ttf'
309312
}
310313
into resourcesStaticDir
311314
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Let frontend overwrite when needed

app/core/src/main/java/stirling/software/SPDF/controller/api/PdfTextEditorCharcodeController.java

Lines changed: 598 additions & 0 deletions
Large diffs are not rendered by default.

app/core/src/main/java/stirling/software/SPDF/service/pdfjson/PdfJsonFontService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,8 @@ public String detectFontFlavor(byte[] fontBytes) {
154154
return "otf";
155155
}
156156
if (signature == 0x74746366) {
157-
return "cff";
157+
log.debug("[FONT-DEBUG] TrueType Collection ('ttcf') font program is unsupported");
158+
return null;
158159
}
159160
return null;
160161
}
@@ -175,7 +176,8 @@ public String detectTrueTypeFormat(byte[] data) {
175176
return "otf";
176177
}
177178
if (signature == 0x74746366) {
178-
return "cff";
179+
log.debug("[FONT-DEBUG] TrueType Collection ('ttcf') FontFile2 is unsupported");
180+
return null;
179181
}
180182
return null;
181183
}

app/core/src/main/resources/logback.xml

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,63 @@
1515
<encoder>
1616
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
1717
</encoder>
18-
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
19-
<fileNamePattern>${LOG_PATH}/auth-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
18+
<!-- SizeAndTime, not Time alone: the size trigger is what stops a
19+
runaway logger filling the disk (see GENERAL appender note).
20+
Archives are gzipped, so 64 MB of them holds far more than a
21+
day. Worst case on disk is one 100 MB live file plus the cap. -->
22+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
23+
<fileNamePattern>${LOG_PATH}/auth-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
24+
<maxFileSize>100MB</maxFileSize>
2025
<maxHistory>7</maxHistory>
2126
<totalSizeCap>64MB</totalSizeCap>
2227
</rollingPolicy>
2328
</appender>
2429

25-
<!-- Rolling File Appender for General Logs -->
30+
<!-- Rolling File Appender for General Logs
31+
32+
Why SizeAndTimeBased + totalSizeCap: a previous build of the v2 PDF
33+
text editor's reverse-CMap probe loop triggered PDSimpleFont to emit
34+
one "No Unicode mapping for .notdef" WARN per probed charcode per
35+
font per request. With TimeBasedRollingPolicy alone there was no
36+
size ceiling; info.log grew to 1.4 GB in a single day before the JVM
37+
choked. The class-level silencer fixes the specific offender, but
38+
this size cap is the defence-in-depth: any future logger that
39+
floods unexpectedly will roll + auto-delete instead of starving
40+
disk + Jetty threads. -->
2641
<appender name="GENERAL" class="ch.qos.logback.core.rolling.RollingFileAppender">
2742
<file>${LOG_PATH}/info.log</file>
2843
<encoder>
2944
<pattern>%d %p %c{1} [%thread] %m%n</pattern>
3045
</encoder>
31-
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
32-
<fileNamePattern>${LOG_PATH}/info-%d{yyyy-MM-dd}.log.gz</fileNamePattern>
46+
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
47+
<fileNamePattern>${LOG_PATH}/info-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
48+
<maxFileSize>100MB</maxFileSize>
3349
<maxHistory>7</maxHistory>
3450
<totalSizeCap>256MB</totalSizeCap>
3551
</rollingPolicy>
3652
</appender>
3753

54+
<!-- Suppress PDFBox PDSimpleFont's per-charcode .notdef WARN.
55+
56+
Required by the v2 PDF text editor's `buildReverseUnicodeMap`
57+
which DELIBERATELY iterates every charcode in 0..0xFFFF to
58+
discover the encoding-to-Unicode map of an embedded subset
59+
font. For any subset font ~99% of those probes hit .notdef,
60+
and the default WARN level for those misses turned info.log
61+
into a 1.4 GB monster overnight.
62+
63+
This declarative logback entry is the SOLE mechanism: it is
64+
visible to ops and revertable via configuration. An earlier
65+
build also mutated this logger's level from a static block in
66+
PdfTextEditorCharcodeController, which silenced the same
67+
warnings JVM-wide with no trace in any config file - that
68+
static block has been removed in favour of this entry. -->
69+
<logger name="org.apache.pdfbox.pdmodel.font.PDSimpleFont"
70+
level="ERROR" additivity="false">
71+
<appender-ref ref="CONSOLE"/>
72+
<appender-ref ref="GENERAL"/>
73+
</logger>
74+
3875
<!-- Root Logger -->
3976
<root level="INFO">
4077
<appender-ref ref="CONSOLE"/>

app/core/src/test/java/stirling/software/SPDF/config/ToolIODeclarationCoverageTest.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ class ToolIODeclarationCoverageTest {
5757
// documents.
5858
"/api/v1/convert/pdf/text-editor",
5959
"/api/v1/convert/text-editor/pdf",
60+
// Charcode lookup for the v2 editor: returns glyph mappings, not a document.
61+
"/api/v1/general/pdf-text-editor",
6062
// Signing sessions, certificate checks and hardware token enumeration; the
6163
// signing tool itself is /api/v1/security/cert-sign, which is declared.
6264
"/api/v1/security/cert-sign/sessions",

0 commit comments

Comments
 (0)