move fonts and auto bold
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+244
-11
@@ -49,7 +49,16 @@ public class PdfJsonFallbackFontService {
|
||||
Map.entry("tinos", "fallback-liberation-serif"),
|
||||
Map.entry("courier", "fallback-liberation-mono"),
|
||||
Map.entry("couriernew", "fallback-liberation-mono"),
|
||||
Map.entry("cousine", "fallback-liberation-mono"));
|
||||
Map.entry("cousine", "fallback-liberation-mono"),
|
||||
// DejaVu fonts - widely used open source fonts
|
||||
Map.entry("dejavu", "fallback-dejavu-sans"),
|
||||
Map.entry("dejavusans", "fallback-dejavu-sans"),
|
||||
Map.entry("dejavuserif", "fallback-dejavu-serif"),
|
||||
Map.entry("dejavumono", "fallback-dejavu-mono"),
|
||||
Map.entry("dejavusansmono", "fallback-dejavu-mono"),
|
||||
// Noto Sans - Google's universal font (use as last resort generic fallback)
|
||||
Map.entry("noto", "fallback-noto-sans"),
|
||||
Map.entry("notosans", "fallback-noto-sans"));
|
||||
|
||||
private static final Map<String, FallbackFontSpec> BUILT_IN_FALLBACK_FONTS =
|
||||
Map.ofEntries(
|
||||
@@ -85,40 +94,172 @@ public class PdfJsonFallbackFontService {
|
||||
Map.entry(
|
||||
"fallback-liberation-sans",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/pdfjs-legacy/standard_fonts/LiberationSans-Regular.ttf",
|
||||
"classpath:/static/fonts/LiberationSans-Regular.ttf",
|
||||
"LiberationSans-Regular",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-sans-bold",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/pdfjs-legacy/standard_fonts/LiberationSans-Bold.ttf",
|
||||
"classpath:/static/fonts/LiberationSans-Bold.ttf",
|
||||
"LiberationSans-Bold",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-sans-italic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/pdfjs-legacy/standard_fonts/LiberationSans-Italic.ttf",
|
||||
"classpath:/static/fonts/LiberationSans-Italic.ttf",
|
||||
"LiberationSans-Italic",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-sans-bolditalic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/pdfjs-legacy/standard_fonts/LiberationSans-BoldItalic.ttf",
|
||||
"classpath:/static/fonts/LiberationSans-BoldItalic.ttf",
|
||||
"LiberationSans-BoldItalic",
|
||||
"ttf")),
|
||||
// Liberation Serif family
|
||||
Map.entry(
|
||||
"fallback-liberation-serif",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/pdfjs-legacy/standard_fonts/LiberationSerif-Regular.ttf",
|
||||
"classpath:/static/fonts/LiberationSerif-Regular.ttf",
|
||||
"LiberationSerif-Regular",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-serif-bold",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/LiberationSerif-Bold.ttf",
|
||||
"LiberationSerif-Bold",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-serif-italic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/LiberationSerif-Italic.ttf",
|
||||
"LiberationSerif-Italic",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-serif-bolditalic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/LiberationSerif-BoldItalic.ttf",
|
||||
"LiberationSerif-BoldItalic",
|
||||
"ttf")),
|
||||
// Liberation Mono family
|
||||
Map.entry(
|
||||
"fallback-liberation-mono",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/pdfjs-legacy/standard_fonts/LiberationMono-Regular.ttf",
|
||||
"classpath:/static/fonts/LiberationMono-Regular.ttf",
|
||||
"LiberationMono-Regular",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-mono-bold",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/LiberationMono-Bold.ttf",
|
||||
"LiberationMono-Bold",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-mono-italic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/LiberationMono-Italic.ttf",
|
||||
"LiberationMono-Italic",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-liberation-mono-bolditalic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/LiberationMono-BoldItalic.ttf",
|
||||
"LiberationMono-BoldItalic",
|
||||
"ttf")),
|
||||
// Noto Sans family (enhanced with weight variants)
|
||||
Map.entry(
|
||||
FALLBACK_FONT_ID,
|
||||
new FallbackFontSpec(
|
||||
DEFAULT_FALLBACK_FONT_LOCATION, "NotoSans-Regular", "ttf")),
|
||||
Map.entry(
|
||||
"fallback-noto-sans-bold",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/NotoSans-Bold.ttf",
|
||||
"NotoSans-Bold",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-noto-sans-italic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/NotoSans-Italic.ttf",
|
||||
"NotoSans-Italic",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-noto-sans-bolditalic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/NotoSans-BoldItalic.ttf",
|
||||
"NotoSans-BoldItalic",
|
||||
"ttf")),
|
||||
// DejaVu Sans family
|
||||
Map.entry(
|
||||
"fallback-dejavu-sans",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSans.ttf", "DejaVuSans", "ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-sans-bold",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSans-Bold.ttf",
|
||||
"DejaVuSans-Bold",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-sans-oblique",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSans-Oblique.ttf",
|
||||
"DejaVuSans-Oblique",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-sans-boldoblique",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSans-BoldOblique.ttf",
|
||||
"DejaVuSans-BoldOblique",
|
||||
"ttf")),
|
||||
// DejaVu Serif family
|
||||
Map.entry(
|
||||
"fallback-dejavu-serif",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSerif.ttf",
|
||||
"DejaVuSerif",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-serif-bold",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSerif-Bold.ttf",
|
||||
"DejaVuSerif-Bold",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-serif-italic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSerif-Italic.ttf",
|
||||
"DejaVuSerif-Italic",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-serif-bolditalic",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSerif-BoldItalic.ttf",
|
||||
"DejaVuSerif-BoldItalic",
|
||||
"ttf")),
|
||||
// DejaVu Mono family
|
||||
Map.entry(
|
||||
"fallback-dejavu-mono",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSansMono.ttf",
|
||||
"DejaVuSansMono",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-mono-bold",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSansMono-Bold.ttf",
|
||||
"DejaVuSansMono-Bold",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-mono-oblique",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSansMono-Oblique.ttf",
|
||||
"DejaVuSansMono-Oblique",
|
||||
"ttf")),
|
||||
Map.entry(
|
||||
"fallback-dejavu-mono-boldoblique",
|
||||
new FallbackFontSpec(
|
||||
"classpath:/static/fonts/DejaVuSansMono-BoldOblique.ttf",
|
||||
"DejaVuSansMono-BoldOblique",
|
||||
"ttf")));
|
||||
|
||||
private final ResourceLoader resourceLoader;
|
||||
@@ -198,7 +339,7 @@ public class PdfJsonFallbackFontService {
|
||||
|
||||
/**
|
||||
* Resolve fallback font ID based on the original font name and code point. Attempts to match
|
||||
* font family for visual consistency.
|
||||
* font family and weight/style for visual consistency.
|
||||
*
|
||||
* @param originalFontName the name of the original font (may be null)
|
||||
* @param codePoint the Unicode code point that needs to be rendered
|
||||
@@ -223,13 +364,22 @@ public class PdfJsonFallbackFontService {
|
||||
|
||||
String aliasedFontId = FONT_NAME_ALIASES.get(baseName);
|
||||
if (aliasedFontId != null) {
|
||||
// Detect weight and style from the normalized font name
|
||||
boolean isBold = detectBold(normalized);
|
||||
boolean isItalic = detectItalic(normalized);
|
||||
|
||||
// Apply weight/style suffix to fallback font ID
|
||||
String styledFontId = applyWeightStyle(aliasedFontId, isBold, isItalic);
|
||||
|
||||
log.debug(
|
||||
"Matched font '{}' (normalized: '{}', base: '{}') to fallback '{}'",
|
||||
"Matched font '{}' (normalized: '{}', base: '{}', bold: {}, italic: {}) to fallback '{}'",
|
||||
originalFontName,
|
||||
normalized,
|
||||
baseName,
|
||||
aliasedFontId);
|
||||
return aliasedFontId;
|
||||
isBold,
|
||||
isItalic,
|
||||
styledFontId);
|
||||
return styledFontId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +387,89 @@ public class PdfJsonFallbackFontService {
|
||||
return resolveFallbackFontId(codePoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if font name indicates bold weight.
|
||||
*
|
||||
* @param normalizedFontName lowercase font name without subset prefix or spaces
|
||||
* @return true if bold weight is detected
|
||||
*/
|
||||
private boolean detectBold(String normalizedFontName) {
|
||||
// Check for explicit bold indicators
|
||||
if (normalizedFontName.contains("bold")
|
||||
|| normalizedFontName.contains("heavy")
|
||||
|| normalizedFontName.contains("black")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for numeric weight indicators (600-900 = bold)
|
||||
// Handles: "Arimo_700wght", "Arial-700", "Font-w700"
|
||||
if (normalizedFontName.matches(".*[_-]?[6-9]00(wght)?.*")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if font name indicates italic/oblique style.
|
||||
*
|
||||
* @param normalizedFontName lowercase font name without subset prefix or spaces
|
||||
* @return true if italic style is detected
|
||||
*/
|
||||
private boolean detectItalic(String normalizedFontName) {
|
||||
return normalizedFontName.contains("italic") || normalizedFontName.contains("oblique");
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply weight/style suffix to fallback font ID.
|
||||
*
|
||||
* <p>Weight/style variants are only applied to font families where we have the actual font
|
||||
* files available. Currently supported: - Liberation Sans: Regular, Bold, Italic, BoldItalic
|
||||
* (full support) - Liberation Serif: Regular, Bold, Italic, BoldItalic (full support) -
|
||||
* Liberation Mono: Regular, Bold, Italic, BoldItalic (full support) - Noto Sans: Regular, Bold,
|
||||
* Italic, BoldItalic (full support) - DejaVu Sans: Regular, Bold, Oblique, BoldOblique (full
|
||||
* support) - DejaVu Serif: Regular, Bold, Italic, BoldItalic (full support) - DejaVu Mono:
|
||||
* Regular, Bold, Oblique, BoldOblique (full support)
|
||||
*
|
||||
* <p>To add weight/style support for additional font families: 1. Download the font files
|
||||
* (Bold, Italic, BoldItalic) to: app/core/src/main/resources/static/fonts/ 2. Register the
|
||||
* variants in BUILT_IN_FALLBACK_FONTS map (see lines 63-267) 3. Update the check below to
|
||||
* include the font family prefix
|
||||
*
|
||||
* @param baseFontId base fallback font ID (e.g., "fallback-liberation-sans")
|
||||
* @param isBold true if bold weight needed
|
||||
* @param isItalic true if italic style needed
|
||||
* @return styled font ID (e.g., "fallback-liberation-sans-bold"), or base ID if variants not
|
||||
* available
|
||||
*/
|
||||
private String applyWeightStyle(String baseFontId, boolean isBold, boolean isItalic) {
|
||||
// Only apply weight/style to font families where we have the font files available
|
||||
// Supported: Liberation (Sans/Serif/Mono), Noto Sans, DejaVu (Sans/Serif/Mono)
|
||||
boolean isSupported =
|
||||
baseFontId.startsWith("fallback-liberation-")
|
||||
|| baseFontId.equals("fallback-noto-sans")
|
||||
|| baseFontId.startsWith("fallback-dejavu-");
|
||||
|
||||
if (!isSupported) {
|
||||
return baseFontId;
|
||||
}
|
||||
|
||||
// DejaVu Sans and Mono use "oblique" instead of "italic"
|
||||
boolean useOblique =
|
||||
baseFontId.equals("fallback-dejavu-sans")
|
||||
|| baseFontId.equals("fallback-dejavu-mono");
|
||||
|
||||
if (isBold && isItalic) {
|
||||
return baseFontId + (useOblique ? "-boldoblique" : "-bolditalic");
|
||||
} else if (isBold) {
|
||||
return baseFontId + "-bold";
|
||||
} else if (isItalic) {
|
||||
return baseFontId + (useOblique ? "-oblique" : "-italic");
|
||||
}
|
||||
|
||||
return baseFontId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve fallback font ID based on Unicode code point properties.
|
||||
*
|
||||
|
||||
@@ -23,15 +23,19 @@
|
||||
- Update `buildFontMap` to resolve aliases when recreating PDFBox fonts, and adjust the front end to load programs via the canonical UID.
|
||||
- Optional: expose a lazy endpoint for the original COS dictionary if the canonical record strips it, so export still reconstructs untouched fonts.
|
||||
|
||||
- **Font Weight Matching for Fallback Fonts**
|
||||
- Font family matching is now implemented (Arial→LiberationSans, Times→LiberationSerif, Courier→LiberationMono).
|
||||
- However, fallback fonts still use Regular weight for all missing glyphs, regardless of the original font weight (e.g., bold text falls back to regular weight).
|
||||
- TODO: Parse weight from font names (e.g., `Arimo_700wght`, `Arial-Bold`, `TimesNewRoman,SemiBold`) and map to corresponding Liberation font variants:
|
||||
- Regular/Normal → LiberationSans-Regular, LiberationSerif-Regular, LiberationMono-Regular
|
||||
- Bold/700 → LiberationSans-Bold, LiberationSerif-Bold, LiberationMono-Bold
|
||||
- Italic/Oblique → LiberationSans-Italic, LiberationSerif-Italic, LiberationMono-Italic
|
||||
- BoldItalic → LiberationSans-BoldItalic, LiberationSerif-BoldItalic, LiberationMono-BoldItalic
|
||||
- Add all Liberation font variants to `BUILT_IN_FALLBACK_FONTS` map with appropriate IDs (e.g., `fallback-liberation-sans-bold`).
|
||||
- Update `resolveFallbackFontId(String originalFontName, int codePoint)` in `PdfJsonFallbackFontService.java` to detect weight/style and return the matching variant ID.
|
||||
- Benefits: Better visual consistency when editing text in bold/italic fonts, as missing characters will match the original weight.
|
||||
- Implementation reference: `app/proprietary/src/main/java/stirling/software/SPDF/service/PdfJsonFallbackFontService.java:186-213`
|
||||
- **Font Weight Matching for Fallback Fonts** ✓ COMPLETED (January 2025)
|
||||
- Font family matching is now implemented:
|
||||
- Liberation fonts (metric-compatible with Microsoft core): Arial/Helvetica→LiberationSans, Times→LiberationSerif, Courier→LiberationMono
|
||||
- DejaVu fonts (widely used open source): DejaVu→DejaVuSans, DejaVuSerif, DejaVuMono
|
||||
- Noto fonts (Google universal font): Noto→NotoSans
|
||||
- Font weight/style matching is now implemented for multiple font families:
|
||||
- Liberation Sans/Serif/Mono: Regular, Bold, Italic, BoldItalic (full support)
|
||||
- Noto Sans: Regular, Bold, Italic, BoldItalic (full support)
|
||||
- DejaVu Sans/Serif/Mono: Regular, Bold, Italic/Oblique, BoldItalic/BoldOblique (full support)
|
||||
- All font variants registered in `BUILT_IN_FALLBACK_FONTS` map (`PdfJsonFallbackFontService.java:63-267`)
|
||||
- Weight/style detection implemented in `resolveFallbackFontId()`:
|
||||
- `detectBold()`: Detects "bold", "heavy", "black", or numeric weights 600-900 (e.g., "700wght")
|
||||
- `detectItalic()`: Detects "italic" or "oblique"
|
||||
- `applyWeightStyle()`: Applies appropriate suffix (handles both "italic" and "oblique" naming)
|
||||
- All fonts consolidated from Type3 library into main fonts directory for unified fallback support
|
||||
- Benefits: Comprehensive visual consistency when editing text in bold/italic fonts across many font families
|
||||
|
||||
Reference in New Issue
Block a user