public int computeMaximumTextSize(final long contentX2) { final int length = getLength(); final long x = getX(); if (contentX2 >= (x + getWidth())) { return length; } final GlyphList gs = getGlyphs(); long runningPos = x; final int offset = getOffset(); final int maxPos = offset + length; for (int i = offset; i < maxPos; i++) { final Glyph g = gs.getGlyph(i); runningPos += RenderableText.convert(g.getWidth()); if (i != offset) { runningPos += g.getSpacing().getMinimum(); } if (runningPos > contentX2) { return Math.max(0, i - offset); } } return length; }
protected void drawText(final RenderableText renderableText, final long contentX2) { if (renderableText.getLength() == 0) { return; } final long posX = renderableText.getX(); final long posY = renderableText.getY(); final float x1 = (float) (StrictGeomUtility.toExternalValue(posX)); final PdfContentByte cb; PdfTextSpec textSpec = (PdfTextSpec) getTextSpec(); if (textSpec == null) { final StyleSheet layoutContext = renderableText.getStyleSheet(); // The code below may be weird, but at least it is predictable weird. final String fontName = getMetaData() .getNormalizedFontFamilyName( (String) layoutContext.getStyleProperty(TextStyleKeys.FONT)); final String encoding = (String) layoutContext.getStyleProperty(TextStyleKeys.FONTENCODING); final float fontSize = (float) layoutContext.getDoubleStyleProperty(TextStyleKeys.FONTSIZE, 10); final boolean embed = globalEmbed || layoutContext.getBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT); final boolean bold = layoutContext.getBooleanStyleProperty(TextStyleKeys.BOLD); final boolean italics = layoutContext.getBooleanStyleProperty(TextStyleKeys.ITALIC); final BaseFontFontMetrics fontMetrics = getMetaData() .getBaseFontFontMetrics(fontName, fontSize, bold, italics, encoding, embed, false); final PdfGraphics2D g2 = (PdfGraphics2D) getGraphics(); final Color cssColor = (Color) layoutContext.getStyleProperty(ElementStyleKeys.PAINT); g2.setPaint(cssColor); g2.setFillPaint(); g2.setStrokePaint(); // final float translateY = (float) affineTransform.getTranslateY(); cb = g2.getRawContentByte(); textSpec = new PdfTextSpec(layoutContext, getMetaData(), g2, fontMetrics, cb); setTextSpec(textSpec); cb.beginText(); cb.setFontAndSize(fontMetrics.getBaseFont(), fontSize); } else { cb = textSpec.getContentByte(); } final BaseFontFontMetrics baseFontRecord = textSpec.getFontMetrics(); final BaseFont baseFont = baseFontRecord.getBaseFont(); final float ascent = baseFont.getFontDescriptor(BaseFont.BBOXURY, textSpec.getFontSize()); final float y2 = (float) (StrictGeomUtility.toExternalValue(posY) + ascent); final float y = globalHeight - y2; final AffineTransform affineTransform = textSpec.getGraphics().getTransform(); final float translateX = (float) affineTransform.getTranslateX(); final FontNativeContext nativeContext = baseFontRecord.getNativeContext(); if (baseFontRecord.isTrueTypeFont() && textSpec.isBold() && nativeContext.isNativeBold() == false) { final float strokeWidth = textSpec.getFontSize() / 30.0f; // right from iText ... if (strokeWidth == 1) { cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL); } else { cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL_STROKE); cb.setLineWidth(strokeWidth); } } else { cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_FILL); } // if the font does not declare to be italics already, emulate it .. if (baseFontRecord.isTrueTypeFont() && textSpec.isItalics() && nativeContext.isNativeItalics() == false) { final float italicAngle = baseFont.getFontDescriptor(BaseFont.ITALICANGLE, textSpec.getFontSize()); if (italicAngle == 0) { // italics requested, but the font itself does not supply italics gylphs. cb.setTextMatrix(1, 0, PdfLogicalPageDrawable.ITALIC_ANGLE, 1, x1 + translateX, y); } else { cb.setTextMatrix(x1 + translateX, y); } } else { cb.setTextMatrix(x1 + translateX, y); } final OutputProcessorMetaData metaData = getMetaData(); final GlyphList gs = renderableText.getGlyphs(); final int offset = renderableText.getOffset(); final CodePointBuffer codePointBuffer = getCodePointBuffer(); if (metaData.isFeatureSupported(OutputProcessorFeature.FAST_FONTRENDERING) && isNormalTextSpacing(renderableText)) { final int maxLength = renderableText.computeMaximumTextSize(contentX2); final String text = gs.getText(renderableText.getOffset(), maxLength, codePointBuffer); cb.showText(text); } else { final PdfTextArray textArray = new PdfTextArray(); final StringBuilder buffer = new StringBuilder(gs.getSize()); final int maxPos = offset + renderableText.computeMaximumTextSize(contentX2); for (int i = offset; i < maxPos; i++) { final Glyph g = gs.getGlyph(i); final Spacing spacing = g.getSpacing(); if (i != offset) { final float optimum = (float) StrictGeomUtility.toFontMetricsValue(spacing.getMinimum()); if (optimum != 0) { textArray.add(buffer.toString()); textArray.add(-optimum / textSpec.getFontSize()); buffer.setLength(0); } } final String text = gs.getGlyphAsString(i, codePointBuffer); buffer.append(text); } if (buffer.length() > 0) { textArray.add(buffer.toString()); } cb.showText(textArray); } }
protected void initialize( final GlyphList glyphs, final int offset, final int length, final ExtendedBaselineInfo baselineInfo, final int script, final boolean forceLinebreak) { if (glyphs == null) { throw new NullPointerException(); } if (forceLinebreak == false && length == 0) { throw new IllegalArgumentException("Do not create zero-length renderable text!"); } if (glyphs.getSize() < (offset + length)) { throw new IllegalArgumentException(); } this.baselineInfo = baselineInfo; this.ltr = true; // this depends on the script value this.script = script; this.glyphs = glyphs; this.offset = offset; this.length = length; this.forceLinebreak = forceLinebreak; normalTextSpacing = true; long wordMinChunkWidth = 0; // long heightAbove = 0; // long heightBelow = 0; long minimumChunkWidth = 0; long realCharTotal = 0; long spacerMin = 0; long spacerMax = 0; long spacerOpt = 0; final int lastPos = Math.min(glyphs.getSize(), offset + length); for (int i = offset; i < lastPos; i++) { final Glyph glyph = glyphs.getGlyph(i); // heightAbove = Math.max(glyph.getBaseLine(), heightAbove); // heightBelow = Math.max(glyph.getHeight() - glyph.getBaseLine(), heightBelow); final int kerning = glyph.getKerning(); final int width = glyph.getWidth(); final long realCharSpace = convert(width - kerning); realCharTotal += realCharSpace; wordMinChunkWidth += realCharSpace; if (i != (lastPos - 1)) { final Spacing spacing = glyph.getSpacing(); spacerMax += spacing.getMaximum(); spacerMin += spacing.getMinimum(); spacerOpt += spacing.getOptimum(); if (normalTextSpacing == true && Spacing.EMPTY_SPACING.equals(spacing) == false) { normalTextSpacing = false; } wordMinChunkWidth += spacing.getMinimum(); } if (glyph.getBreakWeight() > BreakOpportunityProducer.BREAK_CHAR) { minimumChunkWidth = Math.max(minimumChunkWidth, wordMinChunkWidth); wordMinChunkWidth = 0; // Paranoid sanity checks: The word- and linebreaks should have been // replaced by other definitions in the text factory. if (glyph.getBreakWeight() == BreakOpportunityProducer.BREAK_LINE) { throw new IllegalStateException( "A renderable text cannot and must " + "not contain linebreaks."); } } } final long wordMinWidth = spacerMin + realCharTotal; final long wordPrefWidth = spacerOpt + realCharTotal; final long wordMaxWidth = spacerMax + realCharTotal; minimumChunkWidth = Math.max(minimumChunkWidth, wordMinChunkWidth); minimumWidth = wordMinWidth; preferredWidth = wordPrefWidth; setMaximumBoxWidth(wordMaxWidth); setMinimumChunkWidth(minimumChunkWidth); }