You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@pdfbox.apache.org by "pcorless (via GitHub)" <gi...@apache.org> on 2026/09/01 04:32:31 UTC

[PR] Pdfbox 6251 usecmap cidrange precedence [pdfbox]

pcorless opened a new pull request, #515:
URL: https://github.com/apache/pdfbox/pull/515

   # PDFBOX-6251: a CMap's own cid mappings lose to the ones it inherits via usecmap
   
   A CMap that starts with `usecmap` may redefine any code it inherits. It currently can't:
   `useCmap` merges the parent's `cidchar`/`cidrange` mappings into the same collections as the
   importing CMap's own, and since `usecmap` is read from the header, the parent's land in
   `codeToCid`/`codeToCidRanges` first. `toCID` scans the range list first-match, so an inherited
   range that covers a code beats the mapping the CMap declared for it.
   
   ```java
   new CMapParser().parsePredefined("ETenms-B5-H").toCID(0x41, 1);  // 13681, should be 34
   ```
   
   `ETenms-B5-H` exists only to do this override: it uses `ETen-B5-H`, then remaps 0x20-0x7E to the
   proportional latin CIDs 1-95 where the parent has the fullwidth forms at 13648+. Its parsed ranges
   hold both, inherited first:
   
   ```
   [0]   32..126 -> 13648   (from ETen-B5-H, always matches first)
   [226] 32..126 -> 1       (its own, never reached)
   ```
   
   **Scope:** 31 of the 92 bundled CMaps declare mappings of their own on top of a `usecmap`, and all
   31 are affected. Two are horizontal (`ETenms-B5-H`, `UniJIS-UCS2-HW-H`); the other 29 are `-V`
   variants whose own mappings select the vertical forms, so vertical CJK gets horizontal glyphs.
   
   **Symptom:** the wrong glyph, and usually the wrong width with it — `/W` is indexed by CID, so a
   wrong CID falls outside the array and the advance drops to `/DW` = 1000. Latin text renders as
   `J a v a S e r v e r`.
   
   ## Fix
   
   Inherited mappings move to `inheritedCodeToCid`/`inheritedCodeToCidRanges`, consulted only after
   the CMap's own: own map -> own ranges -> inherited map -> inherited ranges.
   
   A `usecmap` chain stays nearest-wins, which needs opposite insertion orders: the maps are keyed, so
   `putAll` is last-wins (deepest first, nearer on top); the range list is first-match, so the nearer
   CMap's ranges go in ahead.
   
   Three things fell out of it:
   
   - **`hasCIDMappings` has to count inherited mappings.** `Identity-V` declares none of its own, so
     once the inherited ones move it would report false and `toCID` would return 0 for every code.
   - **The maps are copied, not shared.** This kills a latent aliasing bug: the old
     `codeToCid.putIfAbsent(length, mappings)` stored the parent's inner map *by reference* when the
     importing CMap had no mapping of that length yet, so a later `addCIDMapping` wrote into the
     cached predefined parent and corrupted it for everyone after.
   - **CID 0 is a mapping, not a miss.** The private range scan returned 0 for both, which with two
     range lists would send a CMap's own `cidrange` to CID 0 through to the inherited mappings. It
     now returns -1 for "no range covers this"; public `toCID` still returns 0.
   
   ## Tests
   
   Seven added to `TestCMapParser`, `mvn -pl fontbox -am test` green (214):
   
   | Test | Covers |
   |---|---|
   | `testUseCmapOwnMappingsWin` | the case above, both `toCID` overloads |
   | `testUseCmapChainKeepsNearestMapping` | a two-level chain stays nearest-wins |
   | `testUseCmapOwnMappingsBeatInheritedRanges` | own cidchar and cidrange beat an inherited range |
   | `testUseCmapOnlyInheritedMappings` | `Identity-V`, everything inherited |
   | `testUseCmapDoesNotShareMappingsWithTheUsedCMap` | the used CMap isn't mutated |
   | `testUseCmapOwnRangeBeatsInheritedChar` | own cidrange beats an inherited cidchar |
   | `testUseCmapOwnMappingToCidZeroIsNotAFallthrough` | CID 0 is a mapping, not a miss |
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@pdfbox.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@pdfbox.apache.org
For additional commands, e-mail: dev-help@pdfbox.apache.org


Re: [PR] PDFBOX-6251 usecmap cidrange precedence [pdfbox]

Posted by "asf-gitbox-commits (via GitHub)" <gi...@apache.org>.
asf-gitbox-commits closed pull request #515: PDFBOX-6251 usecmap cidrange precedence
URL: https://github.com/apache/pdfbox/pull/515


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: dev-unsubscribe@pdfbox.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@pdfbox.apache.org
For additional commands, e-mail: dev-help@pdfbox.apache.org