package org.apache.fop.area;
+import java.util.Locale;
+
// block areas hold either more block areas or line
// areas can also be used as a block spacer
/** if true, allow BPD update */
protected transient boolean allowBPDUpdate = true;
- // a block with may contain the dominant styling info in
- // terms of most lines or blocks with info
+ private Locale locale;
+
+ private String location;
/**
* Add the block to this block area.
return (endIndent != null ? endIndent : 0);
}
+ /**
+ * Sets the language information coming from the FO that generated this area.
+ */
+ public void setLocale(Locale locale) {
+ this.locale = locale;
+ }
+
+ /**
+ * Returns the language information for the FO that generated this area.
+ */
+ public Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Sets the location in the source XML of the FO that generated this area.
+ *
+ * @location the line and column location
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the location in the source XML of the FO that generated this area.
+ *
+ * @return the line and column location, {@code null} if that information is not available
+ */
+ public String getLocation() {
+ return location;
+ }
+
}
/**
* Helper function to return "not supported child" exceptions. Note that the child is valid, just not
* supported yet by FOP.
- *
+ *
* @param loc org.xml.sax.Locator object of the error (*not* parent node)
* @param nsURI namespace URI of incoming invalid node
* @param lName local name (i.e., no prefix) of incoming node
if (loc == null) {
return "Unknown location";
} else {
- return loc.getLineNumber() + "/" + loc.getColumnNumber();
+ return loc.getLineNumber() + ":" + loc.getColumnNumber();
}
}
import org.apache.fop.area.Block;
import org.apache.fop.area.LineArea;
import org.apache.fop.datatypes.Length;
+import org.apache.fop.fo.FONode;
import org.apache.fop.fo.properties.KeepProperty;
import org.apache.fop.fonts.Font;
import org.apache.fop.fonts.FontInfo;
startIndent, endIndent,
this);
+ curBlockArea.setLocale(getBlockFO().getCommonHyphenation().getLocale());
+ curBlockArea.setLocation(FONode.getLocatorString(getBlockFO().getLocator()));
setCurrentArea(curBlockArea); // ??? for generic operations
}
return curBlockArea;
private String id = "";
+ private String location;
+
/**
* Main constructor.
* @param ua the user agent
return id;
}
+ /**
+ * Sets the location of the object enclosing the current content.
+ *
+ * @location the line and column location of the object in the source FO file
+ */
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the location of the object enclosing the current content.
+ *
+ * @return the line and column location of the object in the source FO file,
+ * {@code null} if that information is not available
+ */
+ public String getLocation() {
+ return location;
+ }
+
}
}
saveBlockPosIfTargetable(block);
pushdID(block);
+ IFContext context = documentHandler.getContext();
+ Locale oldLocale = context.getLanguage();
+ context.setLanguage(block.getLocale());
+ String oldLocation = context.getLocation();
+ context.setLocation(block.getLocation());
super.renderBlock(block);
+ context.setLocation(oldLocation);
+ context.setLanguage(oldLocale);
popID(block);
}
* @event.severity WARN
*/
void incorrectEncryptionLength(Object source, int originalValue, int correctedValue);
+
+ /**
+ * The language of a piece of text is unknown.
+ *
+ * @param source the event source
+ * @param location location in the source FO file, if any
+ */
+ void unknownLanguage(Object source, String location);
+
}
<message key="nonFullyResolvedLinkTargets">{count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional.</message>
<message key="nonStandardStructureType">‘{type}’ is not a standard structure type defined by the PDF Reference. Falling back to ‘{fallback}’.</message>
<message key="incorrectEncryptionLength">Encryption length must be a multiple of 8 between 40 and 128. Setting encryption length to {correctedValue} instead of {originalValue}.</message>
+ <message key="unknownLanguage">A piece of text or an image’s alternate text is missing language information [(See position {location})|(No context info available)]</message>
</catalogue>
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.io.IOException;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
import org.w3c.dom.Document;
import org.apache.fop.pdf.PDFXObject;
import org.apache.fop.render.RenderingContext;
import org.apache.fop.render.intermediate.AbstractIFPainter;
+import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFState;
import org.apache.fop.render.intermediate.IFUtil;
private PDFLogicalStructureHandler logicalStructureHandler;
+ private final LanguageAvailabilityChecker languageAvailabilityChecker;
+
+ private static class LanguageAvailabilityChecker {
+
+ private final IFContext context;
+
+ private final Set<String> reportedLocations = new HashSet<String>();
+
+ LanguageAvailabilityChecker(IFContext context) {
+ this.context = context;
+ }
+
+ private void checkLanguageAvailability(String text) {
+ Locale locale = context.getLanguage();
+ if (locale == null && containsLettersOrDigits(text)) {
+ String location = context.getLocation();
+ if (!reportedLocations.contains(location)) {
+ PDFEventProducer.Provider.get(context.getUserAgent().getEventBroadcaster())
+ .unknownLanguage(this, location);
+ reportedLocations.add(location);
+ }
+ }
+ }
+
+ private boolean containsLettersOrDigits(String text) {
+ for (int i = 0; i < text.length(); i++) {
+ if (Character.isLetterOrDigit(text.charAt(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ }
+
/**
* Default constructor.
* @param documentHandler the parent document handler
this.borderPainter = new PDFBorderPainter(this.generator);
this.state = IFState.create();
accessEnabled = this.getUserAgent().isAccessibilityEnabled();
+ languageAvailabilityChecker = accessEnabled
+ ? new LanguageAvailabilityChecker(documentHandler.getContext())
+ : null;
}
/** {@inheritDoc} */
private void prepareImageMCID(PDFStructElem structElem) {
imageMCI = logicalStructureHandler.addImageContentItem(structElem);
+ if (structElem != null) {
+ languageAvailabilityChecker.checkLanguageAvailability((String) structElem.get("Alt"));
+ }
}
/** {@inheritDoc} */
throws IFException {
if (accessEnabled) {
PDFStructElem structElem = (PDFStructElem) getContext().getStructureTreeElement();
+ languageAvailabilityChecker.checkLanguageAvailability(text);
MarkedContentInfo mci = logicalStructureHandler.addTextContentItem(structElem);
if (generator.getTextUtil().isInTextObject()) {
generator.separateTextElements(mci.tag, mci.mcid);
documents. Example: the fix of marks layering will be such a case when it's done.
-->
<release version="FOP Trunk" date="TBD">
+ <action context="Renderers" dev="VH" type="add" fixes-bug="54037">
+ PDF output: Issue a warning when accessibility is enabled and language information is
+ missing.
+ </action>
<action context="Renderers" dev="VH" type="add" fixes-bug="53980">
PDF accessibility: Store language information coming from fo:block or fo:character in the
structure tree.