public void ShowText(IInputBytes bytes) { var currentState = GetCurrentState(); var font = currentState.FontState.FromExtendedGraphicsState ? activeExtendedGraphicsStateFont : resourceStore.GetFont(currentState.FontState.FontName); if (font == null) { throw new InvalidOperationException($"Could not find the font with name {currentState.FontState.FontName} in the resource store. It has not been loaded yet."); } var fontSize = currentState.FontState.FontSize; var horizontalScaling = currentState.FontState.HorizontalScaling / 100m; var characterSpacing = currentState.FontState.CharacterSpacing; var rise = currentState.FontState.Rise; var transformationMatrix = currentState.CurrentTransformationMatrix; var renderingMatrix = TransformationMatrix.FromValues(fontSize * horizontalScaling, 0, 0, fontSize, 0, rise); // TODO: this does not seem correct, produces the correct result for now but we need to revisit. // see: https://stackoverflow.com/questions/48010235/pdf-specification-get-font-size-in-points var pointSize = decimal.Round(rotation.Rotate(transformationMatrix).Multiply(TextMatrices.TextMatrix).Multiply(fontSize).A, 2); while (bytes.MoveNext()) { var code = font.ReadCharacterCode(bytes, out int codeLength); var foundUnicode = font.TryGetUnicode(code, out var unicode); if (!foundUnicode || unicode == null) { log.Warn($"We could not find the corresponding character with code {code} in font {font.Name}."); // Try casting directly to string as in PDFBox 1.8. unicode = new string((char)code, 1); } var wordSpacing = 0m; if (code == ' ' && codeLength == 1) { wordSpacing += GetCurrentState().FontState.WordSpacing; } var textMatrix = TextMatrices.TextMatrix; if (font.IsVertical) { if (!(font is IVerticalWritingSupported verticalFont)) { throw new InvalidOperationException($"Font {font.Name} was in vertical writing mode but did not implement {nameof(IVerticalWritingSupported)}."); } var positionVector = verticalFont.GetPositionVector(code); textMatrix = textMatrix.Translate(positionVector.X, positionVector.Y); } var boundingBox = font.GetBoundingBox(code); var transformedGlyphBounds = rotation.Rotate(transformationMatrix) .Transform(textMatrix .Transform(renderingMatrix .Transform(boundingBox.GlyphBounds))); var transformedPdfBounds = rotation.Rotate(transformationMatrix) .Transform(textMatrix .Transform(renderingMatrix .Transform(new PdfRectangle(0, 0, boundingBox.Width, 0)))); // If the text rendering mode calls for filling, the current nonstroking color in the graphics state is used; // if it calls for stroking, the current stroking color is used. // In modes that perform both filling and stroking, the effect is as if each glyph outline were filled and then stroked in separate operations. // TODO: expose color as something more advanced var color = currentState.FontState.TextRenderingMode != TextRenderingMode.Stroke ? currentState.CurrentNonStrokingColor : currentState.CurrentStrokingColor; ShowGlyph(font, transformedGlyphBounds, transformedPdfBounds.BottomLeft, transformedPdfBounds.BottomRight, transformedPdfBounds.Width, unicode, fontSize, color, pointSize, textSequence); decimal tx, ty; if (font.IsVertical) { var verticalFont = (IVerticalWritingSupported)font; var displacement = verticalFont.GetDisplacementVector(code); tx = 0; ty = (displacement.Y * fontSize) + characterSpacing + wordSpacing; } else { tx = (boundingBox.Width * fontSize + characterSpacing + wordSpacing) * horizontalScaling; ty = 0; } TextMatrices.TextMatrix = TextMatrices.TextMatrix.Translate(tx, ty); } }
public void ShowText(IInputBytes bytes) { var currentState = GetCurrentState(); var font = currentState.FontState.FromExtendedGraphicsState ? activeExtendedGraphicsStateFont : resourceStore.GetFont(currentState.FontState.FontName); if (font == null) { throw new InvalidOperationException($"Could not find the font with name {currentState.FontState.FontName} in the resource store. It has not been loaded yet."); } var fontSize = currentState.FontState.FontSize; var horizontalScaling = currentState.FontState.HorizontalScaling / 100m; var characterSpacing = currentState.FontState.CharacterSpacing; var rise = currentState.FontState.Rise; var transformationMatrix = currentState.CurrentTransformationMatrix; var renderingMatrix = TransformationMatrix.FromValues(fontSize * horizontalScaling, 0, 0, fontSize, 0, rise); // TODO: this does not seem correct, produces the correct result for now but we need to revisit. // see: https://stackoverflow.com/questions/48010235/pdf-specification-get-font-size-in-points var pointSize = decimal.Round(rotation.Rotate(transformationMatrix).Multiply(TextMatrices.TextMatrix).Multiply(fontSize).A, 2); while (bytes.MoveNext()) { var code = font.ReadCharacterCode(bytes, out int codeLength); var foundUnicode = font.TryGetUnicode(code, out var unicode); if (!foundUnicode || unicode == null) { log.Warn($"We could not find the corresponding character with code {code} in font {font.Name}."); // Try casting directly to string as in PDFBox 1.8. unicode = new string((char)code, 1); } var wordSpacing = 0m; if (code == ' ' && codeLength == 1) { wordSpacing += GetCurrentState().FontState.WordSpacing; } if (font.IsVertical) { throw new NotImplementedException("Vertical fonts are currently unsupported, please submit a pull request or issue with an example file."); } var boundingBox = font.GetBoundingBox(code); var transformedGlyphBounds = rotation.Rotate(transformationMatrix) .Transform(TextMatrices.TextMatrix .Transform(renderingMatrix .Transform(boundingBox.GlyphBounds))); var transformedPdfBounds = rotation.Rotate(transformationMatrix) .Transform(TextMatrices.TextMatrix .Transform(renderingMatrix.Transform(new PdfRectangle(0, 0, boundingBox.Width, 0)))); ShowGlyph(font, transformedGlyphBounds, transformedPdfBounds.BottomLeft, transformedPdfBounds.BottomRight, transformedPdfBounds.Width, unicode, fontSize, pointSize); decimal tx, ty; if (font.IsVertical) { tx = 0; ty = boundingBox.GlyphBounds.Height * fontSize + characterSpacing + wordSpacing; } else { tx = (boundingBox.Width * fontSize + characterSpacing + wordSpacing) * horizontalScaling; ty = 0; } var translate = TransformationMatrix.GetTranslationMatrix(tx, ty); TextMatrices.TextMatrix = translate.Multiply(TextMatrices.TextMatrix); } }